TextAnimation.java

package org.opentrafficsim.core.animation;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ImageObserver;
import java.io.Serializable;
import java.rmi.RemoteException;

import javax.media.j3d.Bounds;
import javax.naming.NamingException;

import org.opentrafficsim.core.dsol.OTSSimulatorInterface;

import nl.tudelft.simulation.dsol.animation.Locatable;
import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
import nl.tudelft.simulation.language.d3.BoundingBox;
import nl.tudelft.simulation.language.d3.DirectedPoint;

/**
 * Display a text for another Locatable object.
 * <p>
 * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
 * </p>
 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
 * initial version Dec 11, 2016 <br>
 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
 */
public abstract class TextAnimation implements Locatable, Serializable
{
    /** */
    private static final long serialVersionUID = 20161211L;

    /** the object for which the text is displayed. */
    private final Locatable source;

    /** the text to display. */
    private String text;

    /** the horizontal movement of the text, in meters. */
    private final float dx;

    /** the vertical movement of the text, in meters. */
    private final float dy;

    /** whether to center or not. */
    private final TextAlignment textAlignment;

    /** the color of the text. */
    private Color color;

    /** fontSize the size of the font; default = 2.0 (meters). */
    private final float fontSize;

    /** the animation implementation. */
    private final AnimationImpl animationImpl;

    /** the font. */
    private Font font;

    /** the font rectangle. */
    private Rectangle2D fontRectangle = null;

    /**
     * @param source the object for which the text is displayed
     * @param text the text to display
     * @param dx the horizontal movement of the text, in meters
     * @param dy the vertical movement of the text, in meters
     * @param textAlignment where to place the text
     * @param color the color of the text
     * @param fontSize the size of the font; default = 2.0 (meters)
     * @param simulator the simulator
     * @throws NamingException when animation context cannot be created or retrieved
     * @throws RemoteException - when remote context cannot be found
     */
    @SuppressWarnings("checkstyle:parameternumber")
    public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
            final TextAlignment textAlignment, final Color color, final float fontSize, final OTSSimulatorInterface simulator)
            throws RemoteException, NamingException
    {
        this.source = source;
        this.text = text;
        this.dx = dx;
        this.dy = dy;
        this.textAlignment = textAlignment;
        this.color = color;
        this.fontSize = fontSize;

        this.font = new Font("SansSerif", Font.PLAIN, 2);
        if (this.fontSize != 2.0f)
        {
            this.font = this.font.deriveFont(this.fontSize);
        }

        this.animationImpl = new AnimationImpl(this, simulator);
    }

    /**
     * @param source the object for which the text is displayed
     * @param text the text to display
     * @param dx the horizontal movement of the text, in meters
     * @param dy the vertical movement of the text, in meters
     * @param textAlignment where to place the text
     * @param color the color of the text
     * @param simulator the simulator
     * @throws NamingException when animation context cannot be created or retrieved
     * @throws RemoteException - when remote context cannot be found
     */
    public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
            final TextAlignment textAlignment, final Color color, final OTSSimulatorInterface simulator)
            throws RemoteException, NamingException
    {
        this(source, text, dx, dy, textAlignment, color, 2.0f, simulator);
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("checkstyle:designforextension")
    public DirectedPoint getLocation() throws RemoteException
    {
        // draw always on top.
        DirectedPoint p = this.source.getLocation();
        return new DirectedPoint(p.x, p.y, Double.MAX_VALUE, 0.0, 0.0, p.getRotZ());
    }

    /** {@inheritDoc} */
    @Override
    public final Bounds getBounds() throws RemoteException
    {
        return new BoundingBox(0.0, 0.0, 0.0);
    }

    /**
     * paint() method so it can be overridden or extended.
     * @param graphics the graphics object
     * @param observer the observer
     * @throws RemoteException on network exception
     */
    @SuppressWarnings("checkstyle:designforextension")
    public void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
    {
        graphics.setFont(this.font);
        synchronized (this.font)
        {
            if (this.fontRectangle == null)
            {
                FontMetrics fm = graphics.getFontMetrics();
                this.fontRectangle = fm.getStringBounds(this.text, graphics);
            }
            graphics.setColor(this.color);
            float dxText =
                    this.textAlignment.equals(TextAlignment.LEFT) ? 0.0f : this.textAlignment.equals(TextAlignment.CENTER)
                            ? (float) -this.fontRectangle.getWidth() / 2.0f : (float) -this.fontRectangle.getWidth();
            graphics.drawString(this.text, dxText + this.dx, this.fontSize / 2.0f - this.dy);
        }
    }

    /**
     * Destroy the text animation.
     */
    public final void destroy()
    {
        try
        {
            this.animationImpl.destroy();
        }
        catch (NamingException exception)
        {
            System.err.println("Tried to destroy Text for GTU animation of GTU " + this.source.toString());
        }
    }

    /**
     * Clone the TextAnimation and return a copy for the new source on the new simulator.
     * @param newSource the new source to link to the text animation
     * @param newSimulator the new simulator to register the animation on
     * @return a copy of the TextAnimation
     * @throws RemoteException when remote animation cannot be reached
     * @throws NamingException when animation name cannot be found or bound in the Context
     */
    public abstract TextAnimation clone(final Locatable newSource, final OTSSimulatorInterface newSimulator)
            throws RemoteException, NamingException;

    /**
     * @return source
     */
    protected final Locatable getSource()
    {
        return this.source;
    }

    /**
     * @return dx
     */
    protected final float getDx()
    {
        return this.dx;
    }

    /**
     * @return dy
     */
    protected final float getDy()
    {
        return this.dy;
    }

    /**
     * @return textAlignment
     */
    protected final TextAlignment getTextAlignment()
    {
        return this.textAlignment;
    }

    /**
     * @return fontSize
     */
    protected final float getFontSize()
    {
        return this.fontSize;
    }

    /**
     * @return font
     */
    protected final Font getFont()
    {
        return this.font;
    }

    /**
     * @return current text
     */
    protected final String getText()
    {
        return this.text;
    }

    /**
     * @param text set new text
     */
    protected final void setText(final String text)
    {
        this.text = text;
        synchronized (this.font)
        {
            this.fontRectangle = null;
        }
    }

    /**
     * @return current color
     */
    protected final Color getColor()
    {
        return this.color;
    }

    /**
     * @param color set new color
     */
    protected final void setColor(final Color color)
    {
        this.color = color;
    }

    /**
     * @return Returns the flip.
     */
    public final boolean isFlip()
    {
        return this.animationImpl.isFlip();
    }

    /**
     * @param flip The flip to set.
     */
    public final void setFlip(final boolean flip)
    {
        this.animationImpl.setFlip(flip);
    }

    /**
     * @return Returns the rotate.
     */
    public final boolean isRotate()
    {
        return this.animationImpl.isRotate();
    }

    /**
     * @param rotate The rotate to set.
     */
    public final void setRotate(final boolean rotate)
    {
        this.animationImpl.setRotate(rotate);

    }

    /**
     * @return Returns the scale.
     */
    public final boolean isScale()
    {
        return this.animationImpl.isScale();
    }

    /**
     * @param scale The scale to set.
     */
    public final void setScale(final boolean scale)
    {
        this.animationImpl.setScale(scale);
    }

    /**
     * @return Returns the translate.
     */
    public final boolean isTranslate()
    {
        return this.animationImpl.isTranslate();
    }

    /**
     * @param translate The translate to set.
     */
    public final void setTranslate(final boolean translate)
    {
        this.animationImpl.setTranslate(translate);
    }

    /**
     * The implementation of the text animation. Cloning will be taken care of by the overarching TextAnimation-derived class.
     * <p>
     * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
     * <br>
     * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
     * </p>
     * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
     * initial version Dec 11, 2016 <br>
     * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
     * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
     * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
     */
    private static class AnimationImpl extends Renderable2D implements Serializable
    {
        /** */
        private static final long serialVersionUID = 20170400L;

        /**
         * @param source the source
         * @param simulator the simulator
         * @throws NamingException when animation context cannot be created or retrieved
         * @throws RemoteException - when remote context cannot be found
         */
        AnimationImpl(final Locatable source, final OTSSimulatorInterface simulator) throws NamingException, RemoteException
        {
            super(source, simulator);
        }

        /** {@inheritDoc} */
        @Override
        public final void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
        {
            TextAnimation ta = ((TextAnimation) getSource());
            ta.paint(graphics, observer);
        }

        /** {@inheritDoc} */
        @Override
        public final String toString()
        {
            return "TextAnimation.AnimationImpl []";
        }

    }
}