Package eas.plugins.standard.visualization

Source Code of eas.plugins.standard.visualization.AllroundVideoPlugin

/*
* Datei:            VideoPlugin.java
* Autor(en):        Lukas König
* Java-Version:     6.0
* Erstellt:         23.04.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.plugins.standard.visualization;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingUtilities;

import eas.math.geometry.Polygon2D;
import eas.math.geometry.Rectangle2D;
import eas.math.geometry.Vector2D;
import eas.miscellaneous.StaticMethods;
import eas.plugins.AbstractDefaultPlugin;
import eas.simulation.Wink;
import eas.simulation.agent.AbstractAgent;
import eas.simulation.agent.Evolvable;
import eas.simulation.event.EASEvent;
import eas.simulation.spatial.sim2D.gridSimulation.standardEnvironments.AbstractGridEnvironment;
import eas.simulation.spatial.sim2D.gridSimulation.standardGridObjects.GridObject;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.simulation.spatial.sim2D.standardEnvironments.AbstractEnvironment2D;
import eas.simulation.standardEnvironments.AbstractEnvironment;
import eas.startSetup.ParCollection;
import eas.startSetup.SingleParameter;
import eas.startSetup.parameterDatatypes.Datatypes;
import eas.videoRecording.avi.AVIOutputStream;
import eas.videoRecording.gif.SimpleGIF;

/**
* Implementiert ein Plugin zur Anzeige eines Simulationslaufs bzw. zur
* Erzeugung von AVI- und GIF-Videos aus einem Simulationslauf.
*
* @author Lukas König
*/
public class AllroundVideoPlugin extends AbstractDefaultPlugin<AbstractEnvironment<AbstractAgent<?>>> {

    private static final long serialVersionUID = 7976142351744745219L;

    private long liveCyc;
   
    /**
     * The actual liveFilm parameter.
     */
    private boolean liveFilm;

    /**
     * The live Window the live film is shown in.
     */
    private LiveWindow liveWindow;

    public LiveWindow getLiveWindow() {
        return this.liveWindow;
    }
   
    /**
     * Die Nummer der aktuellen AVI-Datei.
     */
    private int filmNumAVI = 0;

    /**
     * Die Nummer der aktuellen AVI-Datei.
     */
    private int filmNumGIF = 0;

    /**
     * Der Outputstream für den AVI-Film.
     */
    private AVIOutputStream AVIout;

    /**
     * Abfolge von Bildern des Simulationsfeldes für die GIF-Speicherung.
     */
    private transient LinkedList<BufferedImage> bilder;
   
    /**
     * Überschriebener Konstruktor.
     */
    public AllroundVideoPlugin() {
        this.bilder = new LinkedList<BufferedImage>();
    }
   
    /**
     * Parametername für das Zusatzbild.
     */
    public static final String ZUSATZ_BILD_PAR = "showAdditionalAgentInformation";

    /**
     * Parameter name for live films.
     */
    public static final String LIVE_FILM_PAR = "livefilm?";

    /**
     * Parametername für die GIF-Datei.
     */
    public static final String GIF_DATEI_PAR = "giffile";
   
    private int aviDimWidth;
   
    private int aviDimHeight;

    /**
     * Die generischen Parameter beziehen sich auf Dateinamen und
     * Bildwiederholraten der Videos, die im GIF- bzw. AVI-Format gespeichert
     * werden.
     *
     * @return  Liste der Parameter.
     */
    @Override
    public List<SingleParameter> getParameters() {
        ArrayList<SingleParameter> liste = new ArrayList<SingleParameter>();
       
        liste.add(new SingleParameter(
                AllroundVideoPlugin.GIF_DATEI_PAR,
                Datatypes.STRING,
                "null",
                "Der Name der zu erzeugenden GIF-Datei ohne Endung (null, "
                + "falls keine GIF-Datei erzeugt werden soll).",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "avifile",
                Datatypes.STRING,
                "null",
                "Der Name der zu erzeugenden AVI-Datei ohne Endung (null, "
                + "falls keine AVI-Datei erzeugt werden soll).",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "aviwidth",
                Datatypes.integerRange(1, 1600),
                1024,
                "Constant width of the AVI-film.",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "aviheight",
                Datatypes.integerRange(1, 1280),
                768,
                "Constant height of the AVI-film.",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                "aviStoreEveryXCycles",
                Datatypes.LONG,
                5l,
                "Anzahl von Zyklen von einem zum nächsten zu speichernden "
                + "Bild des AVI-Films.",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "gifStoreEveryXCycles",
                Datatypes.LONG,
                5l,
                "Anzahl von Zyklen von einem zum nächsten zu speichernden "
                + "Bild des GIF-Films.",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "aviImagesPerFile",
                Datatypes.LONG,
                5000l,
                "Anzahl von Zyklen, nach denen eine AVI-Datei abgeschlossen "
                + "und ein neuer Film begonnen wird.",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                "gifImagesPerFile",
                Datatypes.LONG,
                500l,
                "Anzahl von Zyklen, nach denen eine GIF-Datei abgeschlossen "
                + "und ein neuer Film begonnen wird.",
                this.id().toUpperCase()));

        liste.add(new SingleParameter(
                AllroundVideoPlugin.ZUSATZ_BILD_PAR,
                Datatypes.fixedStringSet(new String[] {
                        "best",
                        "null",
                        "extern",
                        "agent:0",
                        "agent:1",
                        "agent:2",
                        "agent:3",
                        "agent:4",
                        "agent:5",
                        "agent:6",
                        "agent:7",
                        "agent:8",
                        "agent:9",
                        "agent:10"}),
                "best",
                "Modus für Anzeigen eines zusätzlichen Bildes neben dem Feld ("
                + "best, agent:*int* (Automaten), null, extern)",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                AllroundVideoPlugin.LIVE_FILM_PAR,
                Datatypes.BOOLEAN,
                true,
                "If a live simulation visualization is "
                + "shown during simulation.",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                "framesPerSecond",
                Datatypes.DOUBLE,
                50d,
                "Corresponds to ticks per second in AVI film.",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                "closeWindowsFramesOnTermination",
                Datatypes.BOOLEAN,
                false,
                "If the frame windows should be closed when the simulation terminates.",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                "ShowInsideViewOfMarked",
                Datatypes.BOOLEAN,
                true,
                "Shows the inside view of the marked agent.",
                this.id().toUpperCase()));
       
        liste.add(new SingleParameter(
                ROTATION_SPEED_PAR_NAME,
                Datatypes.DOUBLE,
                0.005,
                "The maximum rotation speed (in RAD) of the camera when visualization is in following mode (set to >= 7 for immediate rotation to agent angle).",
                this.id().toUpperCase(),
                this.getClass()));

        liste.add(new SingleParameter(
                TRANSLATION_ACC_PAR_NAME,
                Datatypes.DOUBLE,
                0.01,
                "The value the translation speed of the camera is accelerated when visualization is in following mode (set to positive infinity for unsmooth following).",
                this.id().toUpperCase(),
                this.getClass()));

        return liste;
    }

    public static final String ROTATION_SPEED_PAR_NAME = "cameraRotationSpeedForFollowingAgent";
    public static final String TRANSLATION_ACC_PAR_NAME = "cameraTranslationAcceleration";
   
    private static double cameraRotationSpeedForFollowingAgent = 0;
    private static double cameraTranslationAcceleration = 0;
   
    public static void setCameraRotationSpeedForFollowingAgent(
            double cameraRotationSpeedForFollowingAgent) {
        AllroundVideoPlugin.cameraRotationSpeedForFollowingAgent = cameraRotationSpeedForFollowingAgent;
    }
   
    public static void setCameraTranslationAcceleration(
            double cameraTranslationAcceleration) {
        AllroundVideoPlugin.cameraTranslationAcceleration = cameraTranslationAcceleration;
    }
   
    /**
     * @return  The id of this plugin.
     */
    @Override
    public String id() {
        return AbstractDefaultPlugin.ALLROUND_PLUGIN_PREFIX + "-videoplugin";
    }

    /**
     * Der konstante Modus "bester": der beste Roboter wird
     * neben dem Feld angezeigt.
     */
    public static final String MODUS_BESTER = "best";

    /**
     * Der konstante Modus "einzelRoboter": ein bestimmter Roboter wird
     * neben dem Feld angezeigt. Benutzung: "agent:*num*"
     */
    public static final String MODUS_EINZELROB = "agent";
   
    /**
     * Der konstante Modus extern ("extern"): Es wird das lokal in VideoPlugin
     * gespeicherte von außen setzbare Bild neben dem Feld angezeigt.
     */
    public static final String MODUS_EXTERN = "extern";

    /**
     * @param agents  A list of agents.
     * @param params  The parameters.
     *
     * @return  The best of the evolvable agents in the list, <code>null</code>
     *          if none of the agents is evolvable.
     */
    private AbstractAgent<?> bester(
            final List<AbstractAgent<?>> agents) {
        AbstractAgent<?> bester = null;
        Evolvable<?> akt = null;
        double bestFit = Double.NEGATIVE_INFINITY;
       
        for (int i = 0; i < agents.size(); i++) {
            AbstractAgent<?> a = agents.get(i);
            if (this.isEvolvable(a)) {
                akt = (Evolvable<?>) a;
                if (akt.getEvaluation() > bestFit) {
                    bester = akt.getThisEvolvableAgent();
                    bestFit = akt.getEvaluation();
                }
            }
        }

        return bester;
    }
   
    /**
     * @param img  An image.
     * @return  A copy of the image.
     */
    private BufferedImage copy(final BufferedImage img) {
        if (img == null) {
            return null;
        }
        BufferedImage copy = new BufferedImage(img.getWidth(), img.getHeight(),
                img.getType());
        copy.createGraphics().drawImage(img, 0, 0, null);
        return copy;
    }
   
    private Color selectedColor = new Color(255, 180, 180, 0);
   
    public Integer getMarkedAgentId() {
        return this.markedAgentId;
    }
   
    /**
     * @param str     Ein zu setzender String.
     * @param env     Die Umgebung, in der sich das zu zeichnende Feld
     *                befindet.
     * @param params  Der Parametersatz.
     *
     * @return  Das BufferedImage des Feldes.
     */
    @SuppressWarnings({ "rawtypes" })
    private BufferedImage erzeugeFeldBild(
//            final String[] str,
            final AbstractEnvironment<AbstractAgent<?>> env,
            final ParCollection params) {
        BufferedImage buffImgFeld = this.copy(env.getOutsideView())// TODO - this is inefficient.
//        BufferedImage buffImgFeld = env.getOutsideView();
        Graphics2D g = buffImgFeld.createGraphics();

        try {
            Rectangle2D rect;
            rect = ((AbstractEnvironment2D) env).getClippingRectangle();
            g.setClip(
                    (int) rect.upperLeftCorner().x,
                    (int) rect.upperLeftCorner().y,
                    (int) (rect.upperLeftCorner().x + rect.getWidth()),
                    (int) (rect.upperLeftCorner().y + rect.getHeight()));
        } catch (Exception e1) {
        }
       
        AbstractAgent bester = this.bester(env.getAgents());
        int radius = 20;
        String zusatzAnzeigeModus = (String) params
                .getParValue(AllroundVideoPlugin.ZUSATZ_BILD_PAR);

        if (this.markedAgentId != null) {
            bester = env.getAgent(this.markedAgentId);
        }
       
        if (zusatzAnzeigeModus.equalsIgnoreCase(
                AllroundVideoPlugin.MODUS_BESTER) && bester != null) {
            g.setColor(Color.red);
            g.setStroke(new BasicStroke(
                    4,
                    BasicStroke.CAP_ROUND,
                    BasicStroke.JOIN_ROUND));
            try {
                g.setColor(Color.red);
                double height = 25;
                double width = 25;
                Vector2D agPos = ((AbstractEnvironment2D) this.currentEnv).getPointInVisualization(((AbstractEnvironment2D) this.currentEnv).getAgentPosition(this.markedAgentId));
                agPos.sub(new Vector2D(width / 2, height / 2));
                g.drawOval((int) agPos.x, (int) agPos.y, (int) (width), (int) (height));
               
                Polygon2D pol2D = ((AbstractEnvironment2D) this.currentEnv).getAgentShapeInVisualization(this.markedAgentId);
                Polygon pol = pol2D.toPol();
                g.setColor(Color.red);
                g.drawPolygon(pol);
            } catch (Exception e) {
                try {
                    g.drawOval(
                            (int) env.getPositionInVisualization(bester.id()).x - radius / 2,
                            (int) env.getPositionInVisualization(bester.id()).y - radius / 2,
                            radius,
                            radius);
                } catch (Exception e1) {}
            }
        } else if (zusatzAnzeigeModus.startsWith(
                AllroundVideoPlugin.MODUS_EINZELROB)) {
            int num = Integer.parseInt(
                    zusatzAnzeigeModus.split(":")[1]);
           
            if (this.markedAgentId != null) {
                num = this.markedAgentId;
            }
           
            g.setColor(Color.red);
            g.setStroke(new BasicStroke(
                    4,
                    BasicStroke.CAP_ROUND,
                    BasicStroke.JOIN_ROUND));
           
            int xMitte = 0;
            int yMitte = 0;
            try {
                xMitte = (int) env.getPositionInVisualization(num).x - radius / 2;
                yMitte = (int) env.getPositionInVisualization(num).y - radius / 2;
            } catch (final Exception e) {
                StaticMethods.log(
                        StaticMethods.LOG_STAGE1,
                        "Agent " + num + " cannot be selected.",
                        params);
            }
           
            try {
                Polygon pol = ((AbstractEnvironment2D) this.currentEnv).getAgentShapeInVisualization(this.markedAgentId).toPol();
                g.setColor(this.selectedColor);
                g.fillPolygon(pol);
                g.setColor(Color.red);
                g.drawPolygon(pol);
            } catch (Exception e) {
                g.drawOval(
                        xMitte,
                        yMitte,
                        radius,
                        radius);
            }
        }

        // Feld beschriften.
//        for (int i = 0; i < str.length; i++) {
//            Color bckGrnd = Color.white;
//            try {
//                bckGrnd = ((AbstractEnvironment2D) this.environment).getBackgroundColor();
//            } catch (Exception e) {
//            }
//            g.setColor(bckGrnd);
//            g.fillRect(10, 10 + 15 * i, 100, 11 + 15 * i);
//            g.setColor(Color.BLUE);
//            g.drawString(str[i], 10, 20 + 15 * i);
//        }
       
        return buffImgFeld;
    }

//    private Color redWeak = new Color(255, 0, 100, 200);
   
    private boolean isEvolvable(AbstractAgent<?> a) {
        return Evolvable.class.isAssignableFrom(a.getClass());
    }
   
    /**
     * Erzeugt ein zusätzlich zum Feld anzuzeigendes Bild. Das kann ein Automat
     * oder eine sonstige grafische Darstellung des Simulationsverlaufs sein.
     *
     * @param umg     Die Umgebung, in der sich das zu zeichnende Feld
     *                befindet.
     * @param params  Der Parametersatz.
     *
     * @return  Das BufferedImage des Automaten.
     */
    private BufferedImage erzeugeAutBild(
            final AbstractEnvironment<AbstractAgent<?>> umg,
            final ParCollection params) {
        AbstractAgent<?> bester = this.bester(umg.getAgents());
        AbstractAgent<?> realBest = bester;
        BufferedImage buffImgAut = null;
        Graphics2D g;
        String zusatzAnzeigeModus = (String) params
            .getParValue(AllroundVideoPlugin.ZUSATZ_BILD_PAR);

        if (zusatzAnzeigeModus.equalsIgnoreCase(
                AllroundVideoPlugin.MODUS_BESTER)
                || zusatzAnzeigeModus.startsWith(
                        AllroundVideoPlugin.MODUS_EINZELROB)) {
            if (zusatzAnzeigeModus.startsWith(
                        AllroundVideoPlugin.MODUS_EINZELROB)) {
                int num = Integer.parseInt(
                        zusatzAnzeigeModus.split(":")[1]);
                try {
                    bester = umg.getAgents().get(num);
                } catch (final Exception e) {
                    StaticMethods.log(
                            StaticMethods.LOG_STAGE1,
                            "Agent " + num + " cannot be selected.",
                            params);
                }
            }

            if (this.markedAgentId != null) {
                AbstractAgent<?> marked = umg.getAgent(this.markedAgentId);
                bester = marked;
            }

            if (bester == null) {
                return null;  
            }

//            buffImgAut = this.copy(bester.getInsideView());
            buffImgAut = bester.getInsideView();
            g = buffImgAut.createGraphics();
            g.setColor(Color.black);
           
            String evol = "";
            if (this.isEvolvable(bester)) {
                evol = " ("
                    + ((Evolvable<?>) bester).getEvaluation()
                    + ")";
            }
           
            if (realBest == bester) {
                g.drawString(
                        bester.getClass().getSimpleName() + " (best): "
                        + bester.id()
                        + evol,
                        10,
                        20);
            } else {
                g.drawString(
                        bester.getClass().getSimpleName() + ": "
                        + bester.id()
                        + evol,
                        10,
                        20);
            }
        } else if (zusatzAnzeigeModus.equalsIgnoreCase(
                AllroundVideoPlugin.MODUS_EXTERN)) {
            return this.darBild;
        }
       
        return buffImgAut;
    }
   
    private transient BufferedImage autImgStored;
   
    /**
     * Mit dem Feld bei Filmerzeugung darzustellendes Bild.
     */
    private transient BufferedImage darBild;

    /**
     * @return Returns the darBild.
     */
    public BufferedImage getDarBild() {
        return this.darBild;
    }

    /**
     * @param bild The darBild to set.
     */
    public void setDarBild(final BufferedImage bild) {
//        this.darBild = this.copy(bild);
        this.darBild = bild;
    }

    /**
     * Gibt ein Bild des aktuellen Feldes zurück.
     *
     * @param str     Links oben zu setzende Strings.
     * @param umg     Die Umgebung, in der sich das zu zeichnende Feld
     *                befindet.
     * @param params  Der Parametersatz.
     *
     * @return  Ein Bild des aktuellen Feldes.
     */
    public BufferedImage erzeugeBild(
//            final String[] str,
            final AbstractEnvironment<AbstractAgent<?>> umg,
            final ParCollection params) {
        Graphics2D g;
        BufferedImage gesamtImg, autImg, feldImg;
       
        feldImg = this.erzeugeFeldBild(umg, params);
       
        if (params.getParValueBoolean("ShowInsideViewOfMarked")) {
            autImg = this.erzeugeAutBild(umg, params);
        } else {
            autImg = null;
        }
       
        this.autImgStored = autImg;
       
        if (feldImg == null) {
            return autImg;
        }
        if (autImg == null) {
            return feldImg;
        }
       
        gesamtImg = new BufferedImage(
                feldImg.getWidth() + autImg.getWidth(),
                Math.max(feldImg.getHeight(), autImg.getHeight()),
                BufferedImage.TYPE_INT_RGB);
        g = gesamtImg.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, gesamtImg.getWidth() - 1, gesamtImg.getHeight() - 1);

        g.drawImage(
                feldImg,
                0,
                0,
                feldImg.getWidth(),
                feldImg.getHeight(),
                null);
        g.drawImage(
                autImg,
                feldImg.getWidth(),
                0,
                autImg.getWidth(),
                autImg.getHeight(),
                null);

        g.setColor(Color.black);
        g.drawRect(0, 0, gesamtImg.getWidth() - 1, gesamtImg.getHeight() - 1);

        return gesamtImg;
    }
   
    /**
     * fügt eine Abblidung des aktuellen Feldes zur AVI-Datei hinzu.
     *
     * @param datNam  Der Name der AVI-Datei.
     * @param simZyk  Die Simulationszyklen.
     * @param umg     Die Umgebung, in der sich das zu zeichnende Feld
     *                befindet.
     * @param params  Der Parametersatz.
     */
    private void aviFrameHinzufuegen(
            final String datNam,
            final AbstractEnvironment<AbstractAgent<?>> umg,
            final ParCollection params) {
        long bildIntAVI = (Long) params.getParValue("aviStoreEveryXCycles");
        double timeBetweenScreenshots = params.getParValueDouble("framesPerSecond");
        BufferedImage gesamtBild;
        BufferedImage alles = new BufferedImage(
                this.aviDimWidth,
                this.aviDimHeight,
                BufferedImage.TYPE_INT_RGB);
       
        String pfad = params.getStdDirectory() + File.separator;
        String datei =
            pfad
            + datNam
            + StaticMethods.normZahl(this.filmNumAVI, 6)
            + ".avi";
        double bilderProSek = timeBetweenScreenshots / bildIntAVI;
//        String[] infos;
       
//        infos = new String[1];
//        infos[0] = "Tick: " + simZyk;
//        infos[1] = "Koll %: " + GlobaleVariablen.getCollCount();
//        infos[2] = "GP %: " + GlobaleVariablen.getGateCount();

        try {
            if (this.AVIout == null) {
                this.AVIout = new AVIOutputStream(new File(datei),
                        AVIOutputStream.VideoFormat.PNG);
                this.AVIout.setVideoCompressionQuality(1f);
   
                this.AVIout.setTimeScale(1);
                this.AVIout.setFrameRate((int) Math.round(bilderProSek));
                StaticMethods.log(
                        StaticMethods.LOG_INFO,
                        "AVI-Datei-Strom erzeugt: " + datei,
                        params);
            }

            gesamtBild = this.erzeugeBild(umg, params);
            Graphics2D g = alles.createGraphics();
            g.setColor(Color.white);
            g.fillRect(0, 0, alles.getWidth(), alles.getHeight());
            g.drawImage(gesamtBild, 0, 0, null);
            this.AVIout.writeFrame(alles);
        } catch (final Exception e) {
            throw new RuntimeException("AVI-Frame nicht gespeichert: " + e);
        }
    }

   
    /**
     * Speichert die Bilderfolge der Simulation als GIF-Datei.
     *
     * @param datNam  Der Dateiname.
     * @param params  Der Parametersatz.
     */
    private void gifSpeichern(
            final String datNam,
            final ParCollection params) {
        if (datNam == null || datNam.equals("null")) {
            return;
        }
       
        double timeBetweenScreenshots = params.getParValueDouble("framesPerSecond");

        long bildIntGIF = (Long) params.getParValue("gifStoreEveryXCycles");
       
        String pfad = params.getStdDirectory() + File.separator;
        String datei =
            pfad
            + datNam
            + StaticMethods.normZahl(this.filmNumGIF, 6)
            + ".gif";
        FileOutputStream fs;
        Image[] imgs = new Image[this.bilder.size()];
        int i = 0;
        double bilderProSek = timeBetweenScreenshots / bildIntGIF;
       
        for (Image img : this.bilder) {
            imgs[i] = img;
            i++;
        }
       
        try {
            fs = new FileOutputStream(datei);
            SimpleGIF.writeAnimatedGIF(
                    imgs,
                    "",
                    false,
                    bilderProSek,
                    fs);
        } catch (final Exception e) {
            throw new RuntimeException("GIF nicht gespeichert: " + e);
        }
       
        this.filmNumGIF++;
       
        StaticMethods.log(
                StaticMethods.LOG_INFO,
                "GIF-Datei gespeichert: " + datei,
                params);
    }

    /**
     * Speichert die Bilderfolge der Simulation als AVI-Datei.
     *
     * @param params  Der Parametersatz.
     */
    private void aviSpeichern(final ParCollection params) {
        String aviDatNam = this.avifile;

        if (aviDatNam == null || aviDatNam.equals("null")) {
            aviDatNam = null;
        }
       
        String pfad = params.getStdDirectory();
        String datNam;
        String datei;
       
        if (aviDatNam != null) {
            datNam = aviDatNam;
        } else {
            return;
        }

        if (this.AVIout != null) {
            try {
                this.AVIout.close();
            } catch(final Exception e) {
                throw new RuntimeException("AVI nicht gespeichert: " + e);
            }
        }
       
        this.AVIout = null;
       
        datei = pfad
            + File.separator
            + datNam
            + StaticMethods.normZahl(this.filmNumAVI, 6)
            + ".avi";
       
        this.filmNumAVI++;

        StaticMethods.log(
                StaticMethods.LOG_INFO,
                "AVI-Datei gespeichert: "
                    + datei
                    + "(Größe: "
                    + new File(datei).length()
                    + " Bytes).",
                params);
    }

    private String avifile;
   
    private String giffile;
   
    private boolean firstTimeFollow = true;
   
    private double currentFollowerSpeed;
    private final double maxSpeed = Double.POSITIVE_INFINITY;
    private double lastDistance = Double.POSITIVE_INFINITY;
//    private Double acceleration = null;
   
    @SuppressWarnings({ "rawtypes" })
    private void followMarkedAgent(AbstractEnvironment<AbstractAgent<?>> umg) {
//        AbstractEnvironment2D<AbstractAgent2D> env2D = null;

        try {
            env2D = (AbstractEnvironment2D) umg;
        } catch (Exception e) {
            return;
        }
       
        try {
            if (this.follow && this.markedAgentId != null) {
//                if (this.acceleration == null) {
//                    this.acceleration = umg.getPars().getParValueDouble(AllroundVideoPlugin.TRANSLATION_ACC_PAR_NAME);
//                }
               
//                env2D.setZoomBoxMiddle(env2D.getAgentPosition(this.markedAgentId));
                Vector2D agentPos = new Vector2D(env2D.getAgentPosition(this.markedAgentId));
                Vector2D zoomBoxPos = new Vector2D(env2D.getZoomBoxMiddle());
                       
                double distance = zoomBoxPos.distance(agentPos);

                if (this.currentFollowerSpeed < this.maxSpeed) {
                    this.currentFollowerSpeed += cameraTranslationAcceleration;
                }
               
                if (lastDistance / distance < 0.1) {
                    this.currentFollowerSpeed = 0;
                }
                   
                double speed = Math.min(distance, this.currentFollowerSpeed);
                Vector2D dir = new Vector2D(agentPos).sub(zoomBoxPos);
                Vector2D newPos = zoomBoxPos.add(dir.setLength(speed));
                this.lastDistance = distance;
               
                env2D.setZoomBoxMiddle(newPos);
               
                this.rotationCenter = env2D.getZoomBoxMiddle(); // env2D.getAgentPosition(this.markedAgentId);
                this.rotationAngle = -env2D.getAgentAngle(this.markedAgentId) * Math.PI / 180 + Math.PI;
            } else if (this.manualCenterPt == null) {
                env2D.resetVisualizationAngle();
                this.firstTimeFollow = true;
                rotationCenter = null;
                rotationAngle = 0;
            }
        } catch (Exception e) {
        }
    }
   
    private Vector2D rotationCenter;
    private double rotationAngle;
//    private Double rotationSpeed = null;
    private AbstractEnvironment2D<?> env2D;
   
    private void setAngleSmooth() {
        if (rotationCenter != null) {
//            if (rotationSpeed == null) {
//                this.cameraRotationSpeedForFollowingAgent = params.getParValueDouble("CameraRotationSpeedForFollowingAgent");
//            }

            double step = cameraRotationSpeedForFollowingAgent;
            double angle = env2D.getVisualizationAngleRAD();
            double gultAngle = this.gultWinkel(angle);
            rotationAngle = this.gultWinkel(rotationAngle);
           
            if (Math.abs(gultAngle - rotationAngle) <= step) {
                env2D.setVisualizationAngle(rotationCenter, rotationAngle);
            } else {
                if (rotationAngle - gultAngle > 0 && rotationAngle - gultAngle < Math.PI) {
                    env2D.setVisualizationAngle(rotationCenter, angle + step);
                } else {
                    env2D.setVisualizationAngle(rotationCenter, angle - step);
                }
            }
           
            if (this.firstTimeFollow) {
                this.setZoom2DPlus();
                this.setZoom2DMinus();
                this.firstTimeFollow = false;
            }
        }
    }
   
    /**
     * Erzeugt einen gültigen Winkel zwischen 0 (=) und 2 * Math.PI (<).
     *
     * @param w  Der zu normalisierende Winkel.
     *
     * @return  Der normalisierte Winkel.
     */
    private double gultWinkel(final double w) {
        double w2 = w;
        while (w2 >= Math.PI * 2) { // Modulo??
            w2 = w2 - Math.PI * 2;
        }
        while (w2 < 0) {
            w2 = w2 + Math.PI * 2;
        }
        return w2;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void adjustZoomBoxMiddle(Vector2D deltaVector) {
        AbstractEnvironment2D<AbstractAgent2D> env2D = null;

        try {
            env2D = (AbstractEnvironment2D) this.environment;
            if (env2D.getZoomBox() == null) {
                Rectangle2D boundingBox = env2D.getBoundingBox(false);
                env2D.setZoomBox(new Rectangle2D(boundingBox.upperLeftCorner(), boundingBox.lowerRightCorner()));
            }
            Vector2D delta = new Vector2D(deltaVector);
            double globalScale = env2D.globalScale();
            delta.scale(Vector2D.NULL_VECTOR, new Vector2D(1 / globalScale, 1 / globalScale));
            Vector2D newMiddle = new Vector2D(env2D.getZoomBoxMiddle());
            newMiddle.translate(delta);
            env2D.setZoomBoxMiddle(newMiddle);
        } catch (Exception e) {

        }
    }
   
    private long lastTime;
   
    /**
     * @param pauseIntervalMs  Pause interval between ticks.
     */
    public void setPauseInterval(int pauseIntervalMs) {
        if (this.liveWindow != null) {
            this.liveWindow.setPauseInterval(pauseIntervalMs);
        }
    }
   
    /**
     * Initiiert die Speicherung von Einzelbildern und der Gesamtfilme.
     */
    @Override
    public synchronized void runDuringSimulation(
            AbstractEnvironment<AbstractAgent<?>> umg,
            Wink simZyk,
            ParCollection params) {
        while (this.liveFilm && this.liveWindow == null) {
            try {Thread.sleep(50);} catch (Exception e) {}
        }

        if (this.liveFilm) {
            double num = liveWindow.pauseInterval / 50 + 1;
   
            if (simZyk.isTick()) {
                for (double i = 1; i < num; i++) {
                    umg.getSimTime().requestNotification(this, simZyk.getCurrentTime() + i * 1 / num);
                }
            }
           
            /*
             * Little pause between ticks.
             */
            if (liveWindow.pauseInterval != 0) {
                long timeElapsed = System.currentTimeMillis() - lastTime;
                try {
                    Thread.sleep((long) (Math.max(0, liveWindow.pauseInterval / num - timeElapsed)));
                } catch (InterruptedException e) {
                }
                this.lastTime = System.currentTimeMillis();
            }
        }
       
        /*
         * Global pause.
         */
        while (this.pause) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
        }
       
        if (this.liveWindow != null) {
          this.liveWindow.showTooltipp(null);
        }
       
        // Follow marked Agent:
        this.followMarkedAgent(umg);
        this.setAngleSmooth();
       
//        String[] infos;
//        infos = new String[1];
//        infos[0] = "Ticks: " + simZyk;
//        infos[1] = "Koll %: " + GlobaleVariablen.getCollCount();
//        infos[2] = "GP %: " + GlobaleVariablen.getGateCount();
        boolean isActive = false;
       
        avifile = (String) params.getParValue("avifile");
        giffile = (String) params.getParValue(GIF_DATEI_PAR);
        long zwSpAVI = (Long) params.getParValue("aviImagesPerFile");
        long zwSpGIF = (Long) params.getParValue("gifImagesPerFile");
        long bildIntAVI = (Long) params.getParValue("aviStoreEveryXCycles");
        long bildIntGIF = (Long) params.getParValue("gifStoreEveryXCycles");
       
        if (avifile.equals("null")) {
            avifile = null;
        } else {
            avifile = umg.getEnvironmentName() + "_" + avifile;
        }

        if (giffile.equals("null")) {
            giffile = null;
        } else {
            giffile = umg.getEnvironmentName() + "_" + giffile;
        }

        if (this.neglectTicks && avifile == null && giffile == null) {
            umg.getSimTime().neglectTicks(this);
        }

        if (avifile != null) {
            isActive = true;
            if (simZyk.isTick() && simZyk.getLastTick() % bildIntAVI == 0) {
                this.aviFrameHinzufuegen(avifile, umg, params);
            }
           
            if (simZyk.isTick() && simZyk.getLastTick() != 0
                    && simZyk.getLastTick() % zwSpAVI == 0 ) {
                this.aviSpeichern(params);
            }
        }

        if (giffile != null) {
            isActive = true;
            if (simZyk.isTick() && simZyk.getLastTick() % bildIntGIF == 0) {
                this.bilder.add(this.erzeugeBild(umg, params));
            }
           
            if (simZyk.isTick() && simZyk.getLastTick() != 0
                    && simZyk.getLastTick() % zwSpGIF == 0 ) {
                this.gifSpeichern(giffile, params);
                this.bilder.clear();
            }
        }
       
        if (this.liveFilm // && simZyk.isTick()
                && simZyk.getLastTick() % this.liveCyc == 0
                && this.liveWindow != null && this.liveWindow.isVisible()) {
           
            isActive = true;
            BufferedImage gesamtBild = this.erzeugeBild(umg, params);
            if (this.liveWindow != null) {
                if (simZyk.getLastTick() == 0) {
//                    this.liveWindow.setSize(
//                            20 + gesamtBild.getWidth(),
//                            20 + gesamtBild.getHeight()
//                            + this.liveWindow.getInsets().top);

//                    this.liveWindow.setExtendedState(JFrame.MAXIMIZED_BOTH);
                }
               
                if (this.currentEnv2D != null) {
                    if (this.markedAgentId == null) {
                        this.currentEnv2D.setScreenWidth(liveWindow.getWidth() - 0);
                    } else {
                        int width = 200;
                        if (this.autImgStored != null) {
                            width = this.autImgStored.getWidth();
                        }
                        this.currentEnv2D.setScreenWidth(liveWindow.getWidth() - width);
                    }
                    this.currentEnv2D.setScreenHeight(liveWindow.getHeight() - 20);
                }
               
                this.liveWindow.metaInfVis.img = gesamtBild;
                this.liveWindow.repaint();
            }
        }
       
        if (isActive) {
            umg.addVisualizer(this);
        } else {
          umg.removeVisualizer(this);
        }
    }
   
    /**
     * Speicherung der Gesamtfilme nach der Simulation.
     */
    @Override
    public void runAfterSimulation(AbstractEnvironment<AbstractAgent<?>> umg, ParCollection params) {
        this.aviSpeichern(params);
        this.gifSpeichern(giffile, params);
        this.terminated = true;
        if (this.liveWindow != null) {
            this.liveWindow.repaint();
        }

        if (params.getParValueBoolean("closeWindowsFramesOnTermination") && this.liveFilm) {
            if (this.liveWindow != null) {
                this.liveWindow.disposeAll();
            }
        }
    }
   
    private boolean terminated = false;
   
    private AbstractEnvironment<AbstractAgent<?>> environment;
   
    public AbstractEnvironment<AbstractAgent<?>> getEnvironment() {
        return this.environment;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void runBeforeSimulation(
            final AbstractEnvironment<AbstractAgent<?>> umg,
            final ParCollection params) {
        this.environment = umg;
        this.aviDimWidth = params.getParValueInt("aviwidth");
        this.aviDimHeight = params.getParValueInt("aviheight");
       
        umg.addVisualizer(this);
        this.currentEnv = umg;
       
        try {
            this.currentEnv2D = (AbstractEnvironment2D) this.currentEnv;
        } catch (Exception e) {
        }
       
        this.liveFilm = params.getParValueBoolean(AllroundVideoPlugin.LIVE_FILM_PAR);
        this.liveCyc = 1;
        if (this.liveFilm) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    AllroundVideoPlugin.this.liveWindow = new LiveWindow(
                            "Live Simulation View",
                            umg,
                            AllroundVideoPlugin.this,
                            params,
                            true);
                }
            });
        }
       
//        /*
//         * Wait a little while to assure that video frame has opened
//         * befor simulation start.
//         */
//        try {
//            Thread.sleep(500);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }
   
    private boolean neglectTicks = false;
   
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void setFitPerfectly(final boolean isPerfect) {
        AbstractEnvironment2D<AbstractAgent2D> env2D = null;

        try {
            env2D = (AbstractEnvironment2D) this.environment;
            env2D.setPerfectFit(isPerfect);
        } catch (Exception e) {
            return;
        }
    }
   
    /**
     * Sets the zoom box of the environment to the specified rectangle - only
     * if the environment is assignable to AbstractEnvironment2D.
     *
     * @param area  The rectangle to set the zoom box to.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public void setZoom(final Rectangle2D area) {
        AbstractEnvironment2D<AbstractAgent2D> env2D = null;

        try {
            env2D = (AbstractEnvironment2D) this.environment;
        } catch (Exception e) {
            return;
        }

        if (area == null) {
            env2D.setZoomBox(null);
        } else {
            env2D.setZoomBox(new Rectangle2D(
                    env2D.getRealPosFromScreenPos(area.upperLeftCorner()),
                    env2D.getRealPosFromScreenPos(area.lowerRightCorner())));
        }
    }
   
    /**
     * @return Returns the neglectTicks.
     */
    public boolean isNeglectTicks() {
        return this.neglectTicks;
    }

    /**
     * @param neglectTicks The neglectTicks to set.
     */
    public void setNeglectTicks(boolean neglectTicks) {
        this.neglectTicks = neglectTicks;
    }

    /**
     * Über diese Methode können Abhängigkeiten zwischen Plugins definiert
     * werden. Die hier zurückgegebene Liste sollte die IDs aller Plugins
     * enthalten, die von diesem Plugin benötigt werden und ohne die kein
     * Start der Simulation möglich sein soll.
     *
     * @return Die Liste benötigter Plugins. Diese Liste kann <code>null</code>
     *         sein, wenn keine abhängigkeiten definiert werden sollen.
     */
    @Override
    public List<String> getRequiredPlugins() {
        return null;
    }
   
    /**
     * The environment.
     */
    private AbstractEnvironment<AbstractAgent<?>> currentEnv;
   
    /**
     * @return Returns the currentEnv.
     */
    public AbstractEnvironment<AbstractAgent<?>> getCurrentEnv() {
        return this.currentEnv;
    }
   
    /**
     * The environment if it is of type AbstractEnvironment2D, null otherwise.
     */
    private AbstractEnvironment2D<AbstractAgent2D<?>> currentEnv2D;

    /**
     * The id of the currently marked agent.
     */
    private Integer markedAgentId = null;
   
    /**
     * The click position on the visualization canvas.
     */
    public void setClickPosition(final int x, final int y) {
        Integer oldMarkedAgentID = this.markedAgentId;
        Vector2D clickPos = new Vector2D(x, y);
        Vector2D agentPos;
        List<AbstractAgent<?>> agents = this.currentEnv.getAgents();
        double min = Double.MAX_VALUE;
        double akt;
        Integer markedID = null;
       
        AbstractAgent<?> agent2D = null;
        try {
            @SuppressWarnings("rawtypes")
            AbstractEnvironment2D env2D = (AbstractEnvironment2D) this.currentEnv;
            agent2D = env2D.getAgentAtPosition(env2D.getRealPosFromScreenPos(new Vector2D(x, y)));
        } catch (Exception e) {
        }

        if (agent2D != null) {
            this.markedAgentId = agent2D.id();
        } else {
            for (AbstractAgent<?> a : agents) {
                agentPos = this.currentEnv.getPositionInVisualization(a.id());
                akt = agentPos.distance(clickPos);
                if (min > akt) {
                    markedID = a.id();
                    min = akt;
                }
            }
           
            this.markedAgentId = markedID;
        }
       
        if (oldMarkedAgentID == null && this.markedAgentId != null && this.follow) {
//            this.setZoom2DPlus();
//            this.setZoom2DPlus();
//            this.setZoom2DPlus();
        }
    }

    public void setMarkedAgentId(Integer markedAgentId) {
        this.markedAgentId = markedAgentId;
    }
   
    public void unmarkSelektion() {
        this.markedAgentId = null;
//        this.setZoom2DMinus();
//        this.setZoom2DMinus();
//        this.setZoom2DMinus();
    }

    public void requestTicks() {
        this.environment.getSimTime().requestTicks(this);
        this.environment.addVisualizer(this);
    }
   
    public void neglectTicks() {
        this.environment.getSimTime().neglectTicks(this);
        this.environment.removeVisualizer(this);
    }
   
    public boolean isSimulationTerminated() {
        return this.terminated;
    }
   
    @Override
    public void handleEvent(
            final EASEvent e,
            final AbstractEnvironment<AbstractAgent<?>> env,
            final Wink lastTick,
            final ParCollection params) {
        if (this.giffile == null && this.avifile == null) {
            this.runDuringSimulation(env, lastTick, params);
        }
    }
   
    private boolean follow = false;
   
    /**
     * @return Returns the follow.
     */
    public boolean isFollow() {
        return this.follow;
    }

    public void setFollowAgent(final boolean followMarkedAgent) {
        this.follow = followMarkedAgent;
    }
   
    public void setZoom2DPlus() {
        try {
            @SuppressWarnings("rawtypes")
            AbstractEnvironment2D env = (AbstractEnvironment2D) this.environment;
            double scale = 1 / 1.2;
            env.setZoomScale(scale);
        } catch (Exception e) {
        }
    }

    public void setZoom2DMinus() {
        try {
            @SuppressWarnings("rawtypes")
            AbstractEnvironment2D env = (AbstractEnvironment2D) this.environment;
            double scale = 1.2;
            env.setZoomScale(scale);
        } catch (Exception e) {
        }
    }

    public double getScale() {
        try {
            @SuppressWarnings("rawtypes")
            AbstractEnvironment2D env = (AbstractEnvironment2D) this.environment;
            return env.globalScale();
        } catch (Exception e) {
            return 1;
        }
    }
   
    private boolean pause = false;
   
    public void pause() {
//        this.environment.getSimTime().setGlobalPause(true);
        this.pause = true;
    }
   
    public void depause() {
        this.pause = false;
        if (this.liveWindow != null) {
            this.liveWindow.requestFocus();
        }
//        this.environment.getSimTime().setGlobalPause(false);
    }
   
    public boolean getPause() {
        return this.pause;
    }
   
    public List<GridObject> getGridObjectsUnderMouse(final int x, final int y) {
//        if (!this.liveWindow.hasFocus()) {
//            return null;
//        }
       
        try {
            AbstractGridEnvironment<?> gridEnv = (AbstractGridEnvironment<?>) this.environment;
            final Vector2D pos = gridEnv.getRealPosFromScreenPos(new Vector2D(x, y));
            LinkedList<GridObject> goList = new LinkedList<GridObject>();
            goList.add(new GridObject() {
                /**
                 *
                 */
                private static final long serialVersionUID = 6936866534584412400L;
                @Override public BufferedImage getOutsideView() {return null;}
                @Override public BufferedImage getInsideView() {return null;}
                @Override public Polygon2D getAgentShape() {return null;}
                @Override public Color getAgentColor() {return null;}
                @Override public String toString() {return "Grid cell <" + (int) (Math.round(pos.x)) + " / " + (int) (Math.round(pos.y)) + ">";}
            });
           
            goList.addAll(gridEnv.getRealFieldPosition((int) Math.round(pos.x), (int) Math.round(pos.y)));
            return goList;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public List<String> getSupportedPlugins() {
        return null;
    }
   
    private Vector2D manualCenterPt = null;
   
    public void setVisualizationAngle(Vector2D centerPoint, double angleRAD) {
        try {
            this.manualCenterPt = centerPoint;
            this.env2D.setVisualizationAngle(centerPoint, angleRAD);
            this.env2D.requestRedraw();
        } catch (Exception e) {}
    }
   
    public void showTooltipps(boolean show) {
        this.liveWindow.setShowTooltipp(show);
    }
   
    public AbstractEnvironment2D<?> getEnv2D() {
        return this.env2D;
    }

    @Override
    public void onSimulationResumed(AbstractEnvironment<AbstractAgent<?>> env,
            Wink resumeTime, ParCollection params) {
        super.onSimulationResumed(env, resumeTime, params);
        if (this.liveFilm) {
            this.liveWindow.setVisible(true);
            this.liveWindow.controlPanel.setVisible(true);
            this.liveWindow.metaInfVis.setVisible(true);
//            if (this.env2D != null) {
//                this.env2D.requestRedraw();
//            }
        }
    }
}
TOP

Related Classes of eas.plugins.standard.visualization.AllroundVideoPlugin

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.