Package eas.simulation.spatial.sim2D.standardEnvironments

Source Code of eas.simulation.spatial.sim2D.standardEnvironments.AbstractEnvironment2DFast

/*
* File name:        AbstractEnvironment2DFast.java (package eas.simulation.spatial.sim2D.standardEnvironments)
* Author(s):        Lukas König
* Java version:     6.0
* Generation date:  14.02.2011 (18:27:21)
*
* (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.simulation.spatial.sim2D.standardEnvironments;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import eas.math.geometry.LineSegment2D;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Rectangle2D;
import eas.math.geometry.Vector2D;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.simulation.standardEnvironments.AbstractEnvironment;
import eas.startSetup.ParCollection;
import eas.startSetup.SingleParameter;
import eas.startSetup.parameterDatatypes.Datatypes;

/**
* This class implements a 2D environment that overrides the methods for
* agent and ray collisions to allow for a more efficient way of calculating
* such collisions.<BR>
* <BR>
* This improvement is achieved by a rigorous simplification of the agent
* shapes, namely allowing rectangles (that can be positioned horizontally or
* vertically) and circles only. Any agent added to <code>this</code> is
* first treated as a rectangle, namely using its own bounding box as shape.
* To make agents being treated as a circle, they have to be specially
* declared. They are treated as circles with a diameter of their bounding
* box width.
*
* @author Lukas König
*
*/
public abstract class AbstractEnvironment2DFast<AgentType extends AbstractAgent2D<?>> extends AbstractEnvironment2D<AgentType> {

    private static final long serialVersionUID = -3622561922205972079L;

    /**
     * The matrix of tiles, each of which holds a list of agents.
     */
    private LinkedList<Integer>[][] tiles;
   
    private Rectangle2D[][] tileCoordinates;
   
    private int tileNumHorizontally;

    private int tileNumVertically;

    private Rectangle2D localBoundingBox;
   
    public AbstractEnvironment2DFast(int id, ParCollection params) {
        this("AbstractEnvironment2DFast_" + id, id, null, params);
    }
   
    /**
     * @param ident
     * @param params
     * @param fatherEnvironment  The environment this environment ist part of
     *                           (may be null if this is a top level
     *                           environment (most common case)).
     */
    @SuppressWarnings("unchecked")
    public AbstractEnvironment2DFast(String ident, int id, AbstractEnvironment<?> fatherEnv, ParCollection params) {
        super(ident, id, fatherEnv, params);

        tileNumHorizontally = params.getParValueInt("TilesHorizontally");
        tileNumVertically = params.getParValueInt("TilesVertically");
        boundingBuffer = params.getParValueVector2D("BoundingBuffer");
        showTiles = params.getParValueBoolean("ShowTiles?");
        epsilon = params.getParValueDouble("Epsilon");
        showRays = params.getParValueBoolean("ShowSensorRays?");
       
        tiles = new LinkedList[tileNumHorizontally][tileNumVertically];
        tileCoordinates = new Rectangle2D[tileNumHorizontally][tileNumVertically];
        agentTouchingTiles = new HashMap<Integer, LinkedList<Vector2D>>();
        this.refreshLocalBoundingBox();
    }

    private Vector2D boundingBuffer;
   
    private Rectangle2D oldBoundingBox;

    private double epsilon;

    /**
     * This method should be fast if no changes to bounding box have been made.
     */
    @SuppressWarnings("unchecked")
    private void refreshLocalBoundingBox() {
        Rectangle2D newBoundingBox = super.getBoundingBox(false);
       
        if (newBoundingBox == null) {
            newBoundingBox = new Rectangle2D(new Vector2D(0, 0), new Vector2D(1, 1));
            this.localBoundingBox = newBoundingBox;
        }
       
        if (this.tileNumHorizontally <= 0) {
//            StaticMethods.logDebug("Number of tiles horizontally must be positive, is: " + this.tileNumHorizontally, this.getParCollection());
            return;
        }
        if (this.tileNumVertically <= 0) {
//            StaticMethods.logDebug("Number of tiles vertically must be positive, is: " + this.tileNumVertically, this.getParCollection());
            return;
        }
       
        // If bounding box changed (==> refresh and reset tile coordinates).
        if (this.oldBoundingBox == null
                || Math.abs(this.oldBoundingBox.upperLeftCorner().x - newBoundingBox.upperLeftCorner().x) > epsilon
                || Math.abs(this.oldBoundingBox.upperLeftCorner().y - newBoundingBox.upperLeftCorner().y) > epsilon
                || Math.abs(this.oldBoundingBox.lowerRightCorner().x - newBoundingBox.lowerRightCorner().x) > epsilon
                || Math.abs(this.oldBoundingBox.lowerRightCorner().y - newBoundingBox.lowerRightCorner().y) > epsilon) {
            double x1 = newBoundingBox.upperLeftCorner().x - boundingBuffer.x,
                   x2 = newBoundingBox.lowerRightCorner().x + boundingBuffer.x,
                   y1 = newBoundingBox.upperLeftCorner().y - boundingBuffer.y,
                   y2 = newBoundingBox.lowerRightCorner().y + boundingBuffer.y;
//            System.out.println("bound");
            tiles = new LinkedList[tileNumHorizontally][tileNumVertically];
            this.localBoundingBox = new Rectangle2D(new Vector2D(x1, y1), new Vector2D(x2, y2));
            this.oldBoundingBox = new Rectangle2D(newBoundingBox);
            this.tileCoordinates = new Rectangle2D[tileNumHorizontally][tileNumVertically];
            this.agentTouchingTiles.clear();
        }
    }
   
    public synchronized Rectangle2D getTileRectangle(final Vector2D tileID) {
//        if (tileID == null
//                || tileID.x < 0
//                || tileID.y < 0
//                || tileID.x >= this.tileNumHorizontally
//                || tileID.y >= this.tileNumVertically) {
//            return null;
//        }

        if (this.tileCoordinates[(int) tileID.x][(int) tileID.y] == null) {
            double left = this.localBoundingBox.upperLeftCorner().x;
            double right = this.localBoundingBox.lowerRightCorner().x;
            double up = this.localBoundingBox.upperLeftCorner().y;
            double down = this.localBoundingBox.lowerRightCorner().y;

            double tileWidth = (right - left) / this.tileNumHorizontally;
            double tileHeight = (down - up) / this.tileNumVertically;

            Vector2D upperLeft = new Vector2D(left + tileWidth * tileID.x, up
                    + tileHeight * tileID.y);
            Vector2D lowerRight = new Vector2D(upperLeft.x + tileWidth,
                    upperLeft.y + tileHeight);

            this.tileCoordinates[(int) tileID.x][(int) tileID.y] = new Rectangle2D(
                    upperLeft, lowerRight);
        }
       
        return this.tileCoordinates[(int) tileID.x][(int) tileID.y];
    }
   
    public Vector2D getTileID(final Vector2D pointInsideTile) {
        if (pointInsideTile == null
                || !this.localBoundingBox.isPointInside(pointInsideTile)) {
            return null;
        }

        double left = this.localBoundingBox.upperLeftCorner().x;
        double right = this.localBoundingBox.lowerRightCorner().x;
        double up = this.localBoundingBox.upperLeftCorner().y;
        double down = this.localBoundingBox.lowerRightCorner().y;

        double tileWidth = (right - left) / this.tileNumHorizontally;
        double tileHeight = (down - up) / this.tileNumVertically;

        int xID = (int) ((pointInsideTile.x - left) / tileWidth);
        int yID = (int) ((pointInsideTile.y - up) / tileHeight);
       
        return new Vector2D(xID, yID);
    }
   
    private HashMap<Integer, LinkedList<Vector2D>> agentTouchingTiles;
   
    public LinkedList<Vector2D> getTilesTouchingAgent(final int agentID) {
        LinkedList<Vector2D> oldTiles = this.agentTouchingTiles.get(agentID);
       
        if (oldTiles != null) {
            return oldTiles;
        }

        try {
            return this.getTilesTouchingAgent(
                    agentID,
                    this.getAgentPosition(agentID),
                    this.getAgentAngle(agentID),
                    this.getAgentScale(agentID));
        } catch (Exception e) {
            return new LinkedList<Vector2D>();
        }
    }

    public synchronized LinkedList<Vector2D> getTilesTouchingAgent(
            final int agentID,
            final Vector2D pos,
            final double angle,
            final Vector2D scale) {
        return this.getTilesTouchingAgent(agentID, this.getAgentShapeInEnvironment(agentID, pos, scale, angle).getBoundingBox());
    }

    public synchronized LinkedList<Vector2D> getTilesTouchingAgent(
            final int agentID,
            final Rectangle2D agentBoundingBox) {
       
        LinkedList<Vector2D> tiles = new LinkedList<Vector2D>();
       
        // TODO: This works fine for all agents, but can be optimized e.g. for circle agents.
        if (this.isAgentShapeRectangle(agentID) || this.isAgentShapeCircle(agentID)) {
            double li = agentBoundingBox.upperLeftCorner().x;
            double re = agentBoundingBox.lowerRightCorner().x;
            double ob = agentBoundingBox.upperLeftCorner().y;
            double un = agentBoundingBox.lowerRightCorner().y;

            Vector2D liobTile = this.getTileID(new Vector2D(li, ob));
            Vector2D reunTile = this.getTileID(new Vector2D(re, un));
           
            if (liobTile != null && reunTile != null) {
                for (double i = liobTile.x; i <= reunTile.x; i++) {
                    for (double j = liobTile.y; j <= reunTile.y; j++) {
                        tiles.add(new Vector2D(i, j));
                    }
                }
            }
           
            this.agentTouchingTiles.put(agentID, tiles);
        } else if (this.isAgentShapeCircle(agentID)) {
           
        }
       
        return tiles;
    }
   
    private void removeAgentFromTileLists(final Integer agentID) {
            this.removeAgentFromTileLists(               
                    agentID,
                    this.getAgentShapeInEnvironment(agentID, this.getAgentPosition(agentID), this.getAgentScale(agentID), this.getAgentAngle(agentID)).getBoundingBox());
    }
   
    private void removeAgentFromTileLists(
            final Integer agentID,
            final Rectangle2D agentBoundingBox) {
        LinkedList<Vector2D> tileList = this.getTilesTouchingAgent(agentID, agentBoundingBox);
       
        for (Vector2D tileID : tileList) {
            int tileX = (int) tileID.x;
            int tileY = (int) tileID.y;
           
            if (this.tiles[tileX][tileY] != null && this.tiles[tileX][tileY].size() > 0) {
//                if (this.tiles[tileX][tileY].contains(agentID)) {
                    this.tiles[tileX][tileY].remove(agentID);
//                }
            }
        }
    }
   
    public HashSet<Integer> getAgentsTouchingTilesFromAll(final List<Vector2D> tileIDs) {
        HashSet<Integer> agentList = new HashSet<Integer>();
       
        for (Vector2D v : tileIDs) {
            agentList.addAll(this.getAgentsTouchingTile(v));
        }
       
        return agentList;
    }

    public synchronized List<Integer> getAgentsTouchingTile(final Vector2D tileID) {
        int tileX = (int) tileID.x;
        int tileY = (int) tileID.y;

        if (this.tiles[tileX][tileY] == null) {
            // This is not efficient and should be called rarely.
           
//            System.out.println("inefficient" + i++);
           
            this.tiles[tileX][tileY] = new LinkedList<Integer>();
            for (AgentType a : this.getAgents()) {
                if (this.getTilesTouchingAgent(a.id()).contains(tileID)) {
                    this.tiles[tileX][tileY].add(a.id());
                }
            }
        }
       
        return this.tiles[tileX][tileY];
    }

    private void registerAgentToTileLists(final int agentID) {
        this.registerAgentToTileLists(
                agentID,
                this.getAgentPosition(agentID),
                this.getAgentAngle(agentID),
                this.getAgentScale(agentID));
    }

    private void registerAgentToTileLists(
            final int agentID,
            final Vector2D pos,
            final double angle,
            final Vector2D scale) {
        LinkedList<Vector2D> tileList = this.getTilesTouchingAgent(agentID, pos, angle, scale);

        for (Vector2D tileID : tileList) {
            int tileX = (int) tileID.x;
            int tileY = (int) tileID.y;
            if (this.tiles[tileX][tileY] == null) {
                // This is inefficient.
                this.getTilesTouchingAgent(agentID, pos, angle, scale);
            } else {
                this.tiles[tileX][tileY].add(agentID);
            }
        }
    }

    @Override
    public synchronized boolean addAgent(AgentType agent,
            Vector2D position, double angle, Vector2D scale) {
        boolean result = super.addAgent(agent, position, angle, scale);
       
        if (result) {
            this.refreshLocalBoundingBox();
            this.registerAgentToTileLists(agent.id(), position, angle, scale);
        }

        return result;
    }

    @Override
    public synchronized boolean setAgentAngleAndPositionAndScaleSimultaneously(
            int agentID, Vector2D pos, double angle, Vector2D scale) {
        this.refreshLocalBoundingBox();
        this.removeAgentFromTileLists(agentID);
        this.registerAgentToTileLists(agentID, pos, angle, scale);
        boolean result = super.setAgentAngleAndPositionAndScaleSimultaneously(agentID, pos, angle, scale);
       
        if (result) {
//            if (this.isCollidingAgent(agentID)) {
//                this.removeAgentFromTileLists(agentID);
//                this.agentTouchingTiles.remove(agentID);
//            }
//            this.refreshLocalBoundingBox();
//            if (this.isCollidingAgent(agentID)) {
//                this.registerAgentToTileLists(agentID, pos, angle, scale);
//            }
        } else {
            this.removeAgentFromTileLists(agentID, this.getAgentShapeInEnvironment(agentID, pos, scale, angle).getBoundingBox());
            this.registerAgentToTileLists(agentID);
        }
       
        return result;
    }
   
    @Override
    public synchronized boolean removeAgent(int agentID) {
        Vector2D pos = this.getAgentPosition(agentID);
        double angle = this.getAgentAngle(agentID);
        Vector2D scale = this.getAgentScale(agentID);
        if (scale == null) {
            scale = new Vector2D(1, 1);
        }
        Rectangle2D boundingBox = this.getAgentShapeInEnvironment(agentID, pos, scale, angle).getBoundingBox();
       
        boolean result = super.removeAgent(agentID);
        if (result) {
            if (this.agentTouchingTiles.containsKey(agentID)) {
                this.removeAgentFromTileLists(agentID, boundingBox);
                this.agentTouchingTiles.remove(agentID);
            }
            this.refreshLocalBoundingBox();
        }
        return result;
    }
   
    @SuppressWarnings("unused")
    private boolean isAgentShapeCircle(final int agentID) {
        return false;
    }
   
    @SuppressWarnings("unused")
    private boolean isAgentShapeRectangle(final int agentID) {
        return true;
    }
   
    @Override
    public boolean collides(
            AgentType a1,
            Vector2D pos1,
            double angle1,
            Vector2D scale1,
            AgentType a2,
            Vector2D pos2,
            double angle2,
            Vector2D scale2) {
        return super.collides(a1, pos1, angle1, scale1, a2, pos2, angle2, scale2);
    }
   
    @Override
    public synchronized boolean collides(
            AgentType agent,
            Vector2D pos,
            double angle,
            Vector2D scale) {
        if (!this.isCollidingAgent(agent.id())) {
            return false;
        }
       
        int agentID = agent.id();
       
        List<Vector2D> tileList = this.getTilesTouchingAgent(agentID, pos, angle, scale);
        HashSet<Integer> agentSet = this.getAgentsTouchingTilesFromAll(tileList);
       
        for (int i : agentSet) {
            try {
                if (this.collides(
                        agent,
                        pos,
                        angle,
                        scale,
                        this.getAgent(i),
                        this.getAgentPosition(i),
                        this.getAgentAngle(i),
                        this.getAgentScale(i))) {
                    return true;
                }
            } catch (Exception e) {
            }
        }

        return false;
    }
   
    private HashSet<Polygon2D> rays = new HashSet<Polygon2D>();
   
    /**
     * TODO: Es kann immer noch passieren, dass an Eckpunkten der Strahl eine
     *       Kachel überspringt! Dafür wird der Korrekturparameter benötigt.
     */
    @Override
    public CollisionData nearestRayCollision(
            Vector2D position,
            Vector2D direction,
            List<AgentType> ignore) {
       
        final double korrektor = Math.max(localBoundingBox.getWidth(), localBoundingBox.getHeight()) / 2000;
       
        CollisionData data;
       
//        data = super.nearestRayCollision(position, direction, ignore);
       
        ArrayList<Integer> ignoreIDs = new ArrayList<Integer>(ignore.size());
       
        for (AgentType agent : ignore) {
            ignoreIDs.add(agent.id());
        }
       
        double maxdist = 0;
        double distanz;
        Rectangle2D bound;
       
        /*
         * Find a point that is clearly beyond the last point occupied
         * by a part of an agent in ray direction. Determine by this a
         * maximal possible distance of a ray collision.
         */
        for (AgentType a : this.getAgents()) {
            bound = a.getAgentShape().getBoundingBox();
            distanz = position.distance(
                    this.getAgentPosition(a.id()))
                        + bound.getWidth()
                        + bound.getHeight();
            if (distanz > maxdist) {
                maxdist = distanz;
            }
        }

        Vector2D furthest = new Vector2D(position);
        Vector2D furthestDir = new Vector2D(direction);
        furthestDir.setLength(maxdist);
        furthest.translate(furthestDir);
        LineSegment2D s = new LineSegment2D(position, furthest);
       
        Vector2D positionTile = this.getTileID(position);
       
        List<Integer> touchingAgents = new LinkedList<Integer>();
        touchingAgents.addAll(this.getAgentsTouchingTile(positionTile));
       
        touchingAgents.removeAll(ignoreIDs);
        CollisionData nearestTouching = this.nearestTouchingIfSameTile(touchingAgents, s, position, positionTile);
        Vector2D lastTile = null;
       
        // Pixellinie auf Tiles zeichnen.
        if (nearestTouching == null || nearestTouching.getAgent() == null) {
            double x1 = s.getAnfPkt().x;
            double y1 = s.getAnfPkt().y;
            double x2 = s.getEndPkt().x;
            double y2 = s.getEndPkt().y;
            int xx1, xx2, yy1, yy2, aktXi, aktYi;
            double aktX, aktY, schrittX, schrittY;

//            double tileLaengeX = this.localBoundingBox.getBreite() / this.tileNumHorizontally;
//            double tileLaengeY = this.localBoundingBox.getHoehe() / this.tileNumVertically;
           
            if (Math.abs(x2 - x1) > Math.abs(y2 - y1)) {
                xx1 = (int) Math.round(x1);
                yy1 = (int) Math.round(y1);
                xx2 = (int) Math.round(x2);
                yy2 = (int) Math.round(y2);
                schrittY = korrektor * ((double) yy2 - (double) yy1) / ((double) xx2 - (double) xx1);
                aktY = yy1;
               
                if (x1 < x2) {
                    for (aktX = xx1; aktX <= xx2; aktX += korrektor) {
                        aktXi = (int) Math.round(aktX);
                        aktYi = (int) Math.round(aktY);

                        positionTile = this.getTileID(new Vector2D(aktXi, aktYi));

                        /*
                         * Dieser Fall tritt auf, wenn keine Agenten innerhalb der
                         * bounding Box getroffen wurden.
                         */
                        if (positionTile == null) {
                            nearestTouching = new CollisionData(null, null);
                            break;
                        }

                        if (!positionTile.equals(lastTile)) {
                            if (showRays && showTiles && isVisualized()) {
                                this.rays.add(this.getTileRectangle(positionTile)
                                        .toPol2D());
                            }
                            touchingAgents = new LinkedList<Integer>();
                            touchingAgents.addAll(this.getAgentsTouchingTile(positionTile));
                            touchingAgents.removeAll(ignoreIDs);
                            nearestTouching = this.nearestTouchingIfSameTile(
                                    touchingAgents, s, position, positionTile);
   
                            if (nearestTouching != null) {
                                break;
                            }
                        }
                       
                        lastTile = positionTile;

                        aktY = aktY + schrittY;
                    }
                } else {
                    for (aktX = xx1; aktX >= xx2; aktX -= korrektor) {
                        aktXi = (int) Math.round(aktX);
                        aktYi = (int) Math.round(aktY);

                        positionTile = this.getTileID(new Vector2D(aktXi, aktYi));

                        /*
                         * Dieser Fall tritt auf, wenn keine Agenten innerhalb der
                         * bounding Box getroffen wurden.
                         */
                        if (positionTile == null) {
                            nearestTouching = new CollisionData(null, null);
                            break;
                        }

                        if (!positionTile.equals(lastTile)) {
                            if (showRays && showTiles && isVisualized()) {
                                this.rays.add(this.getTileRectangle(
                                        positionTile).toPol2D());
                            }
                            touchingAgents = new LinkedList<Integer>();
                            touchingAgents.addAll(this.getAgentsTouchingTile(positionTile));
                            touchingAgents.removeAll(ignoreIDs);
                            nearestTouching = this.nearestTouchingIfSameTile(
                                    touchingAgents, s, position, positionTile);

                            if (nearestTouching != null) {
                                break;
                            }
                        }

                        lastTile = positionTile;

                        aktY = aktY - schrittY;
                    }
                }


            } else {
                xx1 = (int) Math.round(x1);
                yy1 = (int) Math.round(y1);
                xx2 = (int) Math.round(x2);
                yy2 = (int) Math.round(y2);
                schrittX = korrektor * ((double) xx2 - (double) xx1) / ((double) yy2 - (double) yy1);
                aktX = xx1;
               
                if (y1 < y2) {
                    for (aktY = yy1; aktY <= yy2; aktY += korrektor) {
                        aktXi = (int) Math.round(aktX);
                        aktYi = (int) Math.round(aktY);

                        positionTile = this.getTileID(new Vector2D(aktXi, aktYi));

                        /*
                         * Dieser Fall tritt auf, wenn keine Agenten innerhalb der
                         * bounding Box getroffen wurden.
                         */
                        if (positionTile == null) {
                            nearestTouching = new CollisionData(null, null);
                            break;
                        }

                        if (!positionTile.equals(lastTile)) {
                            if (showRays && showTiles && isVisualized()) {
                                this.rays.add(this.getTileRectangle(
                                        positionTile).toPol2D());
                            }
                            touchingAgents = new LinkedList<Integer>();
                            touchingAgents.addAll(this.getAgentsTouchingTile(positionTile));
                            touchingAgents.removeAll(ignoreIDs);
                            nearestTouching = this.nearestTouchingIfSameTile(
                                    touchingAgents, s, position, positionTile);

                            if (nearestTouching != null) {
                                break;
                            }
                        }

                        lastTile = positionTile;

                        aktX = aktX + schrittX;
                    }
                } else {
                    for (aktY = yy1; aktY >= yy2; aktY -= korrektor) {
                        aktXi = (int) Math.round(aktX);
                        aktYi = (int) Math.round(aktY);

                        positionTile = this.getTileID(new Vector2D(aktXi, aktYi));

                        /*
                         * Dieser Fall tritt auf, wenn keine Agenten innerhalb der
                         * bounding Box getroffen wurden.
                         */
                        if (positionTile == null) {
                            nearestTouching = new CollisionData(null, null);
                            break;
                        }

                        if (!positionTile.equals(lastTile)) {
                            if (showRays && showTiles && isVisualized()) {
                                this.rays.add(this.getTileRectangle(
                                        positionTile).toPol2D());
                            }
                            touchingAgents = new LinkedList<Integer>();
                            touchingAgents.addAll(this.getAgentsTouchingTile(positionTile));
                            touchingAgents.removeAll(ignoreIDs);
                            nearestTouching = this.nearestTouchingIfSameTile(
                                    touchingAgents, s, position, positionTile);

                            if (nearestTouching != null) {
                                break;
                            }
                        }

                        lastTile = positionTile;

                        aktX = aktX - schrittX;
                    }
                }
            }
        }
       
        data = nearestTouching;
       
        if (showRays && isVisualized()) {
            if (data.getCollisionPoint() != null) {
                Polygon2D pol = new Polygon2D();
                pol.add(new Vector2D(position));
                pol.add(data.getCollisionPoint());
                rays.add(pol);
            }
        }
       
        return data;
    }
   
    private CollisionData nearestTouchingIfSameTile(
            List<Integer> agents,
            LineSegment2D ray,
            Vector2D position,
            Vector2D currentTileID) {
        double distanz;
        Polygon2D p;
        HashMap<AgentType, LinkedList<Vector2D>> schnittpunkte
            = new HashMap<AgentType, LinkedList<Vector2D>>();

        // Find all intersections of ray with agents.
        for (int agentID : agents) {
            p = this.getAgentShapeInEnvironment(agentID);

            if (this.isTouchable(ray, p.getBoundingBox())) {
                schnittpunkte.put(this.getAgent(agentID), p.intersectAll(ray));
            }
        }

        double minDistance = Double.MAX_VALUE;
        Vector2D minVector = null;
        AgentType minAgent = null;
        for (AgentType a : schnittpunkte.keySet()) {
            for (Vector2D v : schnittpunkte.get(a)) {
                distanz = position.distance(v);
                if (distanz < minDistance) {
                    minDistance = distanz;
                    minVector = v;
                    minAgent = a;
                }
            }
        }

        if (minVector != null && !this.getTileID(minVector).equals(currentTileID)) {
            return null;
        }
       
        if (minAgent == null) {
            return null;
        } else {
            return new CollisionData(minAgent, minVector);
        }
    }
   
    @Override
    public List<SingleParameter> getParameters() {
        List<SingleParameter> list = super.getParameters();
       
        list.add(new SingleParameter("TilesHorizontally", Datatypes.INTEGER, 10, "Number of tiles horizontally for collision calculation.", "ABSTRACT_ENVIRONMENT_2D_FAST"));
        list.add(new SingleParameter("TilesVertically", Datatypes.INTEGER, 10, "Number of tiles vertically for collision calculation.", "ABSTRACT_ENVIRONMENT_2D_FAST"));
        list.add(new SingleParameter("BoundingBuffer", Datatypes.VECTOR2D, new Vector2D(1, 1), "Buffer added left, right, above and below the bounding box.", "ABSTRACT_ENVIRONMENT_2D_FAST"));
        list.add(new SingleParameter("ShowTiles?", Datatypes.BOOLEAN, false, "If the tiles are shown in visualization.", "ABSTRACT_ENVIRONMENT_2D_FAST"));
        list.add(new SingleParameter("Epsilon", Datatypes.DOUBLE, 0.01, "The maximal unrecognized change value to the bounding box (should be less than the bounding buffer).", "ABSTRACT_ENVIRONMENT_2D_FAST"));
        list.add(new SingleParameter("ShowSensorRays?", Datatypes.BOOLEAN, false, "Shows laser sensor rays that have been sent out in the last cycle.", "ABSTRACT_ENVIRONMENT_2D_FAST"));
       
        return list;
    }
   
    private boolean showTiles;
   
    private boolean showRays;
   
    @Override
    public BufferedImage getOutsideView() {
        return this.getOutsideView(null);
    }
   
    @Override
    public synchronized BufferedImage getOutsideView(Graphics2D gg) {
        BufferedImage img = super.getOutsideView(gg);
       
        Graphics2D g;
       
        if (gg == null) {
            g = img.createGraphics();
        } else {
            g = gg;
        }

        if (showTiles) {
        for (int i = 0; i < this.tileNumHorizontally; i++) {
                for (int j = 0; j < this.tileNumVertically; j++) {
                    Rectangle2D r = this.getTileRectangle(new Vector2D(i, j));
                    Polygon2D polInVis = super.getPolygonInVisualization(r.toPol2D());
                    Vector2D pos = polInVis.centerPoint();
                    g.setColor(Color.orange);
                    g.drawPolygon(polInVis.toPol());
//                    g.drawString(i + " / " + j, (float) pos.x, (float) pos.y);
                    g.drawString("" + this.getAgentsTouchingTile(new Vector2D(i, j)).size(), (float) pos.x, (float) pos.y);
                }
            }
        }
       
        g.setColor(Color.blue);
       
        if (showRays) {
            g.drawString("(Rays may be delayed by one simulation cycle.)", 200, 25);
           
            g.setStroke(new BasicStroke(2));
            for (Polygon2D pol : this.rays) {
                g.drawPolygon(this.getPolygonInVisualization(pol).toPol());
            }
        }
       
        rays.clear();
       
//        return super.getOutsideView();
        return img;
    }
}
TOP

Related Classes of eas.simulation.spatial.sim2D.standardEnvironments.AbstractEnvironment2DFast

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.