org.jcurl.core.swing.RockLocationDisplayBase.java Source code

Java tutorial

Introduction

Here is the source code for org.jcurl.core.swing.RockLocationDisplayBase.java

Source

/*
 * jcurl curling simulation framework 
 * Copyright (C) 2005 M. Rohrmoser
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.jcurl.core.swing;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.JComponent;

import org.apache.commons.logging.Log;
import org.jcurl.core.base.PositionSet;
import org.jcurl.core.base.Rock;
import org.jcurl.core.base.RockSet;
import org.jcurl.core.base.TargetDiscrete;
import org.jcurl.core.helpers.JCLoggerFactory;

/**
 * Base for rock location displays. Does all the coordinate transformation math
 * and provides callbacks for actual drawing.
 * 
 * @author <a href="mailto:jcurl@gmx.net">M. Rohrmoser </a>
 * @version $Id: RockLocationDisplayBase.java 230 2006-02-19 12:34:18Z
 *          mrohrmoser $
 */
public abstract class RockLocationDisplayBase extends JComponent implements TargetDiscrete, PropertyChangeListener {
    public static final Map hints = new HashMap();

    private static final Log log = JCLoggerFactory.getLogger(RockLocationDisplayBase.class);

    /**
     * Scale WC a bit to avoid int rounding errors. This is relevant only for
     * int based wc drawing operations e.g. fonts. WC objects (rocks etc.)
     * remain unaffected by this.
     */
    protected static final int SCALE = 1000;

    static {
        // hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION,
        // RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // hints.put(RenderingHints.KEY_COLOR_RENDERING,
        // RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        // hints.put(RenderingHints.KEY_DITHERING,
        // RenderingHints.VALUE_DITHER_ENABLE);
        // hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
        // RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        // hints.put(RenderingHints.KEY_INTERPOLATION,
        // RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        // hints.put(RenderingHints.KEY_RENDERING,
        // RenderingHints.VALUE_RENDER_QUALITY);
        // hints.put(RenderingHints.KEY_STROKE_CONTROL,
        // RenderingHints.VALUE_STROKE_NORMALIZE);
        // hints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
        // RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    }

    private BufferedImage img = null;

    private int oldHei = -1;

    private int oldWid = -1;

    private Orientation orient = Orientation.W;

    protected PositionSet rocks;

    protected final AffineTransform wc_mat = new AffineTransform();

    private Zoomer zoom;

    /**
     * 
     * @param rocks
     *            {@link PositionSet#allHome()}if <code>null</code>
     * @param zoom
     *            {@link Zoomer#HOG2HACK}if <code>null</code>
     */
    public RockLocationDisplayBase(final PositionSet rocks, final Zoomer zoom) {
        this.rocks = rocks == null ? PositionSet.allHome() : rocks;
        this.zoom = zoom == null ? Zoomer.HOUSE2HACK : zoom;
        this.rocks.addPropertyChangeListener(this);
        this.setOpaque(true);
    }

    public RockLocationDisplayBase(Zoomer zoom) {
        this(null, zoom);
    }

    public Component add(Component comp) {
        throw new UnsupportedOperationException("Not allowed.");
    }

    public Component add(Component comp, int index) {
        throw new UnsupportedOperationException("Not allowed.");
    }

    public void add(Component comp, Object constraints) {
        throw new UnsupportedOperationException("Not allowed.");
    }

    public void add(Component comp, Object constraints, int index) {
        throw new UnsupportedOperationException("Not allowed.");
    }

    public Component add(String name, Component comp) {
        throw new UnsupportedOperationException("Not allowed.");
    }

    /**
     * Convert display to world-coordinates.
     * 
     * @param dc
     * @param wc
     * @return world coordinates
     */
    public Point2D dc2wc(final Point2D dc, Point2D wc) {
        try {
            wc = wc_mat.inverseTransform(dc, wc);
            wc.setLocation(wc.getX() / SCALE, wc.getY() / SCALE);
            return wc;
        } catch (NoninvertibleTransformException e) {
            throw new RuntimeException("Why uninvertible?", e);
        }
    }

    public void exportPng(File dst) throws IOException {
        final BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2 = (Graphics2D) img.getGraphics();
        {
            final Map hints = new HashMap();
            hints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            hints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            hints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            hints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
            hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2.addRenderingHints(hints);
        }
        this.paintComponent(g2);
        g2.dispose();
        if (!dst.getName().endsWith(".png"))
            dst = new File(dst.getName() + ".png");
        ImageIO.write(img, "png", dst);
    }

    public Dimension getMaximumSize() {
        return super.getMaximumSize();
    }

    public Dimension getMinimumSize() {
        return super.getMinimumSize();
    }

    public Dimension getPreferredSize() {
        return super.getPreferredSize();
    }

    public Zoomer getZoom() {
        return zoom;
    }

    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        if (log.isDebugEnabled())
            log.debug("[" + this.getX() + ", " + this.getY() + ", " + this.getWidth() + ", " + this.getHeight()
                    + "]");
        final Graphics2D g2 = (Graphics2D) g;
        final AffineTransform dc_mat = g2.getTransform();
        g2.setRenderingHints(hints);
        final int w = this.getWidth();
        final int h = this.getHeight();

        // paint WC stuff (ice and rocks)
        if (zoom.hasChanged() || oldWid != w || oldHei != h) {
            // either the wc viewport, fixpoint or dc viewport has changed:
            // re-compute the transformation
            wc_mat.setToIdentity();
            zoom.computeWctoDcTrafo(this.getBounds(), orient, true, wc_mat);
            oldWid = w;
            oldHei = h;
            // re-build the background image
            img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            final Graphics2D gi = (Graphics2D) img.getGraphics();
            gi.setRenderingHints(hints);
            paintIceDC(gi);
        }
        g2.drawImage(img, null, 0, 0);
        paintRocksDC(g2);

        g2.setTransform(dc_mat);
    }

    /**
     * Callback to draw the ice (background).
     * 
     * @param g2
     */
    protected abstract void paintIceDC(final Graphics2D g2);

    /**
     * Basic rock drawing method: draw one single rock at (0,0,0). Assumes the
     * correct transformation display- to rock-coodinates is set already.
     * 
     * @param g
     *            transformed graphics context.
     * @param isDark
     *            rock color
     * @param idx
     *            rock index
     * @see #paintRockWC(Graphics2D, Rock, boolean, int)
     */
    protected abstract void paintRockRC(final Graphics2D g, final boolean isDark, final int idx);

    /**
     * Callback to draw the rocks. Sets the transformation wc to dc and
     * delegates to {@link #paintRocksWC(Graphics2D, PositionSet, int)}.
     * 
     * @param g2
     *            graphics context.
     */
    protected void paintRocksDC(final Graphics2D g2) {
        g2.transform(wc_mat);
        // all rocks
        paintRocksWC(g2, rocks, PositionSet.ALL_MASK);
    }

    /**
     * Paint all rocks given by the mask.
     * 
     * @see #paintRockWC(Graphics2D, Rock, boolean, int)
     * @param g
     *            graphics context
     * @param rocks
     *            locations
     * @param mask
     *            bit field which rocks to paint. {@link PositionSet#ALL_MASK}
     */
    protected void paintRocksWC(final Graphics2D g, final PositionSet rocks, int mask) {
        if ((mask & RockSet.ALL_MASK) == 0)
            return;
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--) {
            if (((mask >> i) & 1) == 1)
                paintRockWC(g, rocks.getRock(i), (i % 2) == 0, i / 2);
        }
    }

    /**
     * Draw one rock at it's wc position. Builds the coordinate transform and
     * calls {@link #paintRockRC(Graphics2D, boolean, int)}.
     * 
     * @param g
     * @param rock
     * @param isDark
     * @param idx
     * @see RockPainter#paintRockRC(Graphics2D, boolean, int)
     */
    protected void paintRockWC(final Graphics2D g, final Rock rock, final boolean isDark, final int idx) {
        final AffineTransform t = g.getTransform();
        g.translate(JCurlDisplay.SCALE * rock.getX(), JCurlDisplay.SCALE * rock.getY());
        g.rotate(Math.PI + rock.getZ());
        // make the right-handed coordinate system left handed again (for
        // un-flipped text display)
        g.scale(-1, 1);
        paintRockRC(g, isDark, idx);
        g.setTransform(t);
    }

    /**
     * Property (rocks) changed.
     * 
     * @param evt
     * @see #setPos(double, PositionSet)
     */
    public void propertyChange(PropertyChangeEvent evt) {
        final Object tmp = evt.getNewValue();
        if (tmp == null || PositionSet.class.isAssignableFrom(tmp.getClass())) {
            setPos(0, (PositionSet) tmp);
        }
    }

    /**
     * Triggers a repaint.
     * 
     * @see #setPos(double, PositionSet, int)
     * @param time
     *            [sec] unused
     * @param rocks
     *            rocks' locations.
     */
    public void setPos(final double time, final PositionSet rocks) {
        setPos(time, rocks, RockSet.ALL_MASK);
    }

    /**
     * Triggers a repaint. If the given <code>rocks</code> reference changed
     * the listeners are updated.
     * 
     * @param time
     *            [sec] unused
     * @param rocks
     *            the rocks' locations
     * @param discontinuous
     *            bitmask of discontinuous locations
     */
    public void setPos(final double time, final PositionSet rocks, final int discontinuous) {
        // this.time = time;
        if (this.rocks != rocks) {
            if (this.rocks != null) {
                this.rocks.removePropertyChangeListener(this);
            }
            rocks.addPropertyChangeListener(this);
        }
        this.rocks = rocks;
        this.repaint();
    }

    /**
     * Convert world- to display coordinates.
     * 
     * @param wc
     * @param dc
     * @return display coordinates
     */
    public Point2D wc2dc(final Point2D wc, Point2D dc) {
        if (dc == null)
            dc = new Point2D.Float();
        dc.setLocation(wc.getX() * SCALE, wc.getY() * SCALE);
        return wc_mat.transform(dc, dc);
    }
}