Package eas.simulation.spatial.sim2D.standardScenes

Source Code of eas.simulation.spatial.sim2D.standardScenes.Scene2D

/*
* File name:    Scene2D.java
* Java version: 6.0
* Author(s):    Lukas König
* File created: 09.10.2010
*
* (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.standardScenes;

import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import eas.math.geometry.Rectangle2D;
import eas.math.geometry.Vector2D;
import eas.miscellaneous.StaticMethods;
import eas.simulation.Wink;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.simulation.spatial.sim2D.standardEnvironments.AbstractEnvironment2D;
import eas.startSetup.ParCollection;

/**
* @author Lukas König
*
*/
public class Scene2D<AgentType extends AbstractAgent2D<?>> implements Serializable {

    private static final long serialVersionUID = 1939377131147263727L;
    private HashMap<Integer, AgentType> agentList;
    private HashMap<Integer, Vector2D> agentPositions;
    private HashMap<Integer, Double> agentAngles;
    private HashMap<Integer, Vector2D> agentScales;
    private HashMap<Integer, Boolean> collidingAgents;
   
    private AffineTransform sceneTransformation;
    private double sceneAngle = 0;
    private Vector2D sceneScale = new Vector2D(1, 1);

    private String identity;

    private ParCollection pars;
   
    public Scene2D(final String ident, final ParCollection params) {
        this.pars = params;
        this.identity = ident;
        this.clearScene();
    }

    private int firstFreeID = 0;
   
    private void findNextFreeID() {
        while (this.agentAngles.get(this.firstFreeID) != null) {
            this.firstFreeID++;
        }
    }
   
    /**
     * Note that the id of the agent
     * may change due to id collisions. The new id of the agent can be
     * received by the method <code>agent.id()</code> or by the return
     * value of this method.
     *
     * @param agent
     * @param agentPosition
     * @param agentViewAngle
     * @param agentScale
     *
     * @return  The id this agent was set to in the scene.
     */
    public int addAgent(
            final AgentType agent,
            final Vector2D agentPosition,
            final double agentViewAngle,
            final Vector2D agentScale) {
        int id;
        id = agent.id();
       
        // ID existiert bereits.
        if (this.agentAngles.get(id) != null) {
            id = this.firstFreeID;
            if (this.pars.getParValueBoolean("ProduceWarningWhenAgentIDChanges?")) {
                StaticMethods.logWarning(
                        this.identity + " --> Agent ID " + agent.id()
                        + " was replaced by ID " + id + ".",
                        this.pars);
            }
            agent.setID(id);
        }
       
        this.agentList.put(agent.id(), agent);
        this.agentAngles.put(id, agentViewAngle);
        this.agentPositions.put(id, agentPosition);
        this.agentScales.put(id, agentScale);
        this.collidingAgents.put(id, false);

        if (id == this.firstFreeID) {
            this.findNextFreeID();
        }
       
        return id;
    }

    /**
     * Note that adding a colliding agent to a scene will not check for
     * spatial collisions with other agents in the scene. However, when
     * placing the scene in an environment a colliding agent may not be
     * placed into the environment if it would collide with an existing
     * agent.
     *
     * Note that the id of the agent may change due to id collisions.
     * The new id of the agent can be received by the method
     * <code>agent.id()</code> or by the return value of this method.
     *
     * @param agent
     * @param agentPosition
     * @param agentViewAngle
     * @param agentScale
     *
     * @return  The id this agent was set to in the scene.
     */
    public int addCollidingAgent(
            final AgentType agent,
            final Vector2D agentPosition,
            final double agentViewAngle,
            final Vector2D agentScale) {
        int id;
        id = agent.id();
       
        // ID existiert bereits.
        if (this.agentAngles.get(id) != null) {
            id = this.firstFreeID;
            if (this.pars.getParValueBoolean("ProduceWarningWhenAgentIDChanges?")) {
                StaticMethods.logWarning(
                        this.identity + " --> Agent ID " + agent.id()
                        + " was replaced by ID " + id + ".",
                        this.pars);
            }
            agent.setID(id);
        }

        this.agentList.put(agent.id(), agent);
        this.agentAngles.put(agent.id(), agentViewAngle);
        this.agentPositions.put(agent.id(), agentPosition);
        this.agentScales.put(agent.id(), agentScale);
        this.collidingAgents.put(agent.id(), true);

        if (id == this.firstFreeID) {
            this.findNextFreeID();
        }
       
        return id;
    }
   
    public void addScene(final Scene2D<AgentType> scene) {
        int id;
       
        for (AgentType a : scene.getAgentList()) {
            id = a.id();
           
            if (scene.isColliding(id)) {
                this.addCollidingAgent(
                        a,
                        scene.getAgentPosition(id),
                        scene.getAgentAngle(id),
                        scene.getAgentScale(id));
            } else {
                this.addAgent(
                        a,
                        scene.getAgentPosition(id),
                        scene.getAgentAngle(id),
                        scene.getAgentScale(id));
            }
        }
    }
   
    public void clearScene() {
        if (this.agentList != null) {
            ArrayList<Integer> agentIds = new ArrayList<Integer>(this.agentList.keySet());
           
            for (int id : agentIds) {
                this.removeAgent(id);
            }
        }
       
        this.agentList = new HashMap<Integer, AgentType>();
        this.agentAngles = new HashMap<Integer, Double>();
        this.agentPositions = new HashMap<Integer, Vector2D>();
        this.agentScales = new HashMap<Integer, Vector2D>();
        this.collidingAgents = new HashMap<Integer, Boolean>();
        this.sceneTransformation = new AffineTransform();
        this.sceneAngle = 0;
    }
   
    public void removeAgent(final Integer agentID) {
        this.agentList.remove(agentID);
        this.agentAngles.remove(agentID);
        this.agentPositions.remove(agentID);
        this.agentScales.remove(agentID);
        this.collidingAgents.remove(agentID);
        if (agentID < this.firstFreeID) {
            this.firstFreeID = agentID;
        }
    }
   
    public void rotateScene(final double angleDEG) {
        this.sceneTransformation.rotate(angleDEG / 180 * Math.PI);
        this.sceneAngle += angleDEG;
    }

    public void scaleScene(final Vector2D scaling) {
        this.sceneTransformation.scale(scaling.x, scaling.y);
        this.sceneScale.x *= scaling.x;
        this.sceneScale.y *= scaling.y;
    }
   
    public void scaleScene(final double scaling) {
        this.sceneTransformation.scale(scaling, scaling);
        this.sceneScale.x *= scaling;
        this.sceneScale.y *= scaling;
    }
   
    public void translateScene(final Vector2D translation) {
        this.sceneTransformation.translate(translation.x, translation.y);
    }

    public static final int FITTING_MODE_FIT_UPPER_LEFT = 0;
    public static final int FITTING_MODE_FIT_LOWER_RIGHT = 1;
    public static final int FITTING_MODE_FIT_CENTER = 2;
   
    public void fitInBoundingRectangle(final Vector2D upperLeft, final Vector2D lowerRight) {
        this.fitInBoundingRectangle(upperLeft, lowerRight, 0);
    }
   
    /**
     * Makes the scene fit into the given bounding rectangle. There, the scenes
     * width / height relation is preserved. The scene is scaled to fit in the
     * rectangle with one dimension possible being not tight to the bounds, and
     * the upper-left corner is set to the upper-left corner given by the
     * parameter (note that the scene is not centered in the non-fitting
     * dimension).<BR>
     * <BR>
     * CAUTION: The scene may be rotated, but not scaled or translated before.
     *
     * @param upperLeft   The upper-left corner of the box the scene should fit
     *                    in. This is also the upper-left corner of the bounding
     *                    box of the transformed scene. (Standard - may vary
     *                    depending on fitting mode.)
     * @param lowerRight  The lower-right corner of the box the scene should fit
     *                    in. This is not neccessarily the lower-right corner
     *                    of the bounding box of the target scene in BOTH
     *                    dimensions (it is, however, in at least one dimension).
     *                    (Standard - may vary depending on fitting mode.)
     * @param
     *                   
     */
    public void fitInBoundingRectangle(final Vector2D upperLeft, final Vector2D lowerRight, final int mode) {
        Scene2D<AgentType> scaledScene = new Scene2D<AgentType>("", this.pars);
        Scene2D<AgentType> sceneCopy = new Scene2D<AgentType>("", this.pars);
        scaledScene.addScene(this);
        sceneCopy.addScene(this);
       
        Rectangle2D boundingBox = this.getBoundingBox();
        double currentWidth = boundingBox.getWidth();
        double currentHeight = boundingBox.getHeight();
        double desiredWidth = Math.abs(lowerRight.x - upperLeft.x);
        double desiredHeight = Math.abs(lowerRight.y - upperLeft.y);
        Vector2D newUpperLeftCorner = new Vector2D(Math.min(upperLeft.x, lowerRight.x), Math.min(upperLeft.y, lowerRight.y));
        Vector2D newLowerRightCorner = new Vector2D(Math.max(upperLeft.x, lowerRight.x), Math.max(upperLeft.y, lowerRight.y));
       
        double scaleX = desiredWidth / currentWidth;
        double scaleY = desiredHeight / currentHeight;
        double scale = Math.min(scaleX, scaleY);
       
        scaledScene.scaleScene(scale);
       
        double transX, transY;
       
        if (mode == Scene2D.FITTING_MODE_FIT_UPPER_LEFT) {
            Vector2D currentUpperLeftCorner = scaledScene.getBoundingBox().upperLeftCorner();
            transX = newUpperLeftCorner.x - currentUpperLeftCorner.x;
            transY = newUpperLeftCorner.y - currentUpperLeftCorner.y;
        } else if (mode == Scene2D.FITTING_MODE_FIT_LOWER_RIGHT) {
            Vector2D currentLowerRightCorner = scaledScene.getBoundingBox().lowerRightCorner();
            transX = newLowerRightCorner.x - currentLowerRightCorner.x;
            transY = newLowerRightCorner.y - currentLowerRightCorner.y;
        } else { // First case again - center mode not yet implemented!
            Vector2D currentUpperLeftCorner = scaledScene.getBoundingBox().upperLeftCorner();
            transX = newUpperLeftCorner.x - currentUpperLeftCorner.x;
            transY = newUpperLeftCorner.y - currentUpperLeftCorner.y;
        }
       
        Vector2D translation = new Vector2D(transX, transY);
       
        sceneCopy.translateScene(translation);
        sceneCopy.scaleScene(scale);
       
        this.clearScene();
        this.addScene(sceneCopy);
    }
   
    /**
     * Preserves the current scene, meaning that the scene transformations
     * are fixed. After invocation of this method, all transformations
     * performed on the transformation matrix are transferred to the agent
     * positions, angles and scales, and a reset is performed on the
     * transformation matrix.
     *
     * TODO: Some bug present? ==> Test!
     */
    public void preserveScene() {
        Scene2D<AgentType> sceneCopy = new Scene2D<AgentType>("", this.pars);
        sceneCopy.addScene(this);
        this.clearScene();
        this.addScene(sceneCopy);
    }
   
    public Collection<AgentType> getAgentList() {
        return this.agentList.values();
    }

    /*
     *  { m00 m10 m01 m11 m02 m12 }.
     *  { m00 m10 m01 m11 }.
     *      [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
     *      [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
     *      [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]
     */
    public Vector2D getAgentPosition(final int agentID) {
        double[] matrix = new double[6];
        this.sceneTransformation.getMatrix(matrix);
        Vector2D pos = new Vector2D(this.agentPositions.get(agentID));
        double m00 = matrix[0];
        double m10 = matrix[1];
        double m01 = matrix[2];
        double m11 = matrix[3];
        double m02 = matrix[4];
        double m12 = matrix[5];
        double x = pos.x;
        double y = pos.y;
        pos.x = m00 * x + m01 * y + m02;
        pos.y = m10 * x + m11 * y + m12;
        return pos;
    }

    public double getAgentAngle(final int agentID) {
        return this.agentAngles.get(agentID) + this.sceneAngle;
    }
   
    public Vector2D getAgentScale(final int agentID) {
        Vector2D agentScale = this.agentScales.get(agentID);
        Vector2D completeScale = new Vector2D(
                this.sceneScale.x,
                this.sceneScale.y);
        completeScale.x *= agentScale.x;
        completeScale.y *= agentScale.y;
        return completeScale;
    }
   
    public String getIdentity() {
        return this.identity;
    }
   
    /**
     * @param agentID
     *
     * @return  The agents collision status (true => is colliding). null if
     *          agent does not exist.
     */
    public Boolean isColliding(final int agentID) {
        return this.collidingAgents.get(agentID);
    }

    /**
     * Generates a BufferedImage view of the scene by creating an
     * Abstractenvironment2D, adding this scene to it and returning the
     * fieldView of the environment.
     *
     * @return  A field view of the environment.
     */
    public BufferedImage getSceneFiew() {
        AbstractEnvironment2D<AgentType> env
            = new AbstractEnvironment2D<AgentType>(0, this.pars) {
           
            /**
                 *
                 */
                private static final long serialVersionUID = 5497508512710657553L;

            @Override
            public void step(Wink simTime) {
               
            }
        };
       
        env.addScene(this);
        return env.getOutsideView();
    }
   
    /**
     * @return  The bounding box of this scene (null if no agent has been
     *          placed so far).
     *          The calculation is not efficient as the scene is converted
     *          into an environment and its bounding box calculation is used.
     *          TODO: Make efficient.
     */
    public Rectangle2D getBoundingBox() {
        AbstractEnvironment2D<AgentType> env = new AbstractEnvironment2D<AgentType>(0, this.pars) {

            /**
             *
             */
            private static final long serialVersionUID = 6941143245566787898L;

            @Override
            public void step(Wink simTime) {

            }
        };

        env.addScene(this);
        return env.getBoundingBox(true);
    }
}
TOP

Related Classes of eas.simulation.spatial.sim2D.standardScenes.Scene2D

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.