All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tiled.core.TileLayer Maven / Gradle / Ivy

There is a newer version: 1.4.2
Show newest version
/*-
 * #%L
 * This file is part of libtiled-java.
 * %%
 * Copyright (C) 2004 - 2016 Thorbjørn Lindeijer 
 * Copyright (C) 2004 - 2016 Adam Turk 
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */
package tiled.core;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.util.HashMap;
import java.util.Properties;

/**
 * A TileLayer is a specialized MapLayer, used for tracking two dimensional tile
 * data.
 *
 * @author Thorbjørn Lindeijer
 * @author Adam Turk
 * @version 0.17
 */
public class TileLayer extends MapLayer {

    protected Tile[][] map;
    protected HashMap tileInstanceProperties = new HashMap<>();

    /**
     * 

getTileInstancePropertiesAt.

* * @param x a int. * @param y a int. * @return a {@link java.util.Properties} object. */ public Properties getTileInstancePropertiesAt(int x, int y) { if (!bounds.contains(x, y)) { return null; } Object key = new Point(x, y); return tileInstanceProperties.get(key); } /** *

setTileInstancePropertiesAt.

* * @param x a int. * @param y a int. * @param tip a {@link java.util.Properties} object. */ public void setTileInstancePropertiesAt(int x, int y, Properties tip) { if (bounds.contains(x, y)) { Object key = new Point(x, y); tileInstanceProperties.put(key, tip); } } /** * Default constructor. */ public TileLayer() { } /** * Construct a TileLayer from the given width and height. * * @param w width in tiles * @param h height in tiles */ public TileLayer(int w, int h) { super(w, h); } /** * Create a tile layer using the given bounds. * * @param r the bounds of the tile layer. */ public TileLayer(Rectangle r) { super(r); } /** * @param m the map this layer is part of */ TileLayer(Map m) { super(m); } /** *

Constructor for TileLayer.

* * @param m the map this layer is part of * @param w width in tiles * @param h height in tiles */ public TileLayer(Map m, int w, int h) { super(w, h); setMap(m); } /** * {@inheritDoc} * * Rotates the layer by the given Euler angle. * @see MapLayer#rotate(int) */ @Override public void rotate(int angle) { Tile[][] trans; int xtrans = 0, ytrans = 0; switch (angle) { case ROTATE_90: trans = new Tile[bounds.width][bounds.height]; xtrans = bounds.height - 1; break; case ROTATE_180: trans = new Tile[bounds.height][bounds.width]; xtrans = bounds.width - 1; ytrans = bounds.height - 1; break; case ROTATE_270: trans = new Tile[bounds.width][bounds.height]; ytrans = bounds.width - 1; break; default: System.out.println("Unsupported rotation (" + angle + ")"); return; } double ra = Math.toRadians(angle); int cos_angle = (int) Math.round(Math.cos(ra)); int sin_angle = (int) Math.round(Math.sin(ra)); for (int y = 0; y < bounds.height; y++) { for (int x = 0; x < bounds.width; x++) { int xrot = x * cos_angle - y * sin_angle; int yrot = x * sin_angle + y * cos_angle; trans[yrot + ytrans][xrot + xtrans] = getTileAt(x + bounds.x, y + bounds.y); } } bounds.width = trans[0].length; bounds.height = trans.length; map = trans; } /** * {@inheritDoc} * * Performs a mirroring function on the layer data. Two orientations are * allowed: vertical and horizontal. * * Example: layer.mirror(MapLayer.MIRROR_VERTICAL); will mirror * the layer data around a horizontal axis. */ @Override public void mirror(int dir) { Tile[][] mirror = new Tile[bounds.height][bounds.width]; for (int y = 0; y < bounds.height; y++) { for (int x = 0; x < bounds.width; x++) { if (dir == MIRROR_VERTICAL) { mirror[y][x] = map[bounds.height - 1 - y][x]; } else { mirror[y][x] = map[y][bounds.width - 1 - x]; } } } map = mirror; } /** * Checks to see if the given Tile is used anywhere in the layer. * * @param t a Tile object to check for * @return true if the Tile is used at least once, * false otherwise. */ public boolean isUsed(Tile t) { for (int y = 0; y < bounds.height; y++) { for (int x = 0; x < bounds.width; x++) { if (map[y][x] == t) { return true; } } } return false; } /** {@inheritDoc} */ @Override public boolean isEmpty() { for (int p = 0; p < 2; p++) { for (int y = 0; y < bounds.height; y++) { for (int x = p; x < bounds.width; x += 2) { if (map[y][x] != null) { return false; } } } } return true; } /** * {@inheritDoc} * * Sets the bounds (in tiles) to the specified Rectangle. Caution: * this causes a reallocation of the data array, and all previous data is * lost. * @see MapLayer#setBounds */ @Override protected void setBounds(Rectangle bounds) { super.setBounds(bounds); map = new Tile[bounds.height][bounds.width]; // Tile instance properties is null when this method is called from // the constructor of MapLayer if (tileInstanceProperties != null) { tileInstanceProperties.clear(); } } /** * {@inheritDoc} * * Creates a diff of the two layers, ml is considered the * significant difference. */ @Override public MapLayer createDiff(MapLayer ml) { if (ml == null) { return null; } if (ml instanceof TileLayer) { Rectangle r = null; for (int y = bounds.y; y < bounds.height + bounds.y; y++) { for (int x = bounds.x; x < bounds.width + bounds.x; x++) { if (((TileLayer) ml).getTileAt(x, y) != getTileAt(x, y)) { if (r != null) { r.add(x, y); } else { r = new Rectangle(new Point(x, y)); } } } } if (r != null) { MapLayer diff = new TileLayer( new Rectangle(r.x, r.y, r.width + 1, r.height + 1)); diff.copyFrom(ml); return diff; } else { return new TileLayer(); } } else { return null; } } /** * Removes any occurences of the given tile from this map layer. If layer is * locked, an exception is thrown. * * @param tile the Tile to be removed */ public void removeTile(Tile tile) { for (int y = 0; y < bounds.height; y++) { for (int x = 0; x < bounds.width; x++) { if (map[y][x] == tile) { setTileAt(x + bounds.x, y + bounds.y, null); } } } } /** * Sets the tile at the specified position. Does nothing if (tx, ty) falls * outside of this layer. * * @param tx x position of tile * @param ty y position of tile * @param ti the tile object to place */ public void setTileAt(int tx, int ty, Tile ti) { if (bounds.contains(tx, ty)) { map[ty - bounds.y][tx - bounds.x] = ti; } } /** * Returns the tile at the specified position. * * @param tx Tile-space x coordinate * @param ty Tile-space y coordinate * @return tile at position (tx, ty) or null when (tx, ty) is * outside this layer */ public Tile getTileAt(int tx, int ty) { return (bounds.contains(tx, ty)) ? map[ty - bounds.y][tx - bounds.x] : null; } /** * Returns the first occurrence (using top down, left to right search) of * the given tile. * * @param t the {@link tiled.core.Tile} to look for * @return A java.awt.Point instance of the first instance of t, or * null if it is not found */ public Point locationOf(Tile t) { for (int y = bounds.y; y < bounds.height + bounds.y; y++) { for (int x = bounds.x; x < bounds.width + bounds.x; x++) { if (getTileAt(x, y) == t) { return new Point(x, y); } } } return null; } /** * Replaces all occurrences of the Tile find with the Tile * replace in the entire layer * * @param find the tile to replace * @param replace the replacement tile */ public void replaceTile(Tile find, Tile replace) { for (int y = bounds.y; y < bounds.y + bounds.height; y++) { for (int x = bounds.x; x < bounds.x + bounds.width; x++) { if (getTileAt(x, y) == find) { setTileAt(x, y, replace); } } } } /** {@inheritDoc} */ @Override public void mergeOnto(MapLayer other) { for (int y = bounds.y; y < bounds.y + bounds.height; y++) { for (int x = bounds.x; x < bounds.x + bounds.width; x++) { Tile tile = getTileAt(x, y); if (tile != null) { ((TileLayer) other).setTileAt(x, y, tile); } } } } /** * {@inheritDoc} * * Like mergeOnto, but will only copy the area specified. * @see TileLayer#mergeOnto(MapLayer) */ @Override public void maskedMergeOnto(MapLayer other, Area mask) { Rectangle boundBox = mask.getBounds(); for (int y = boundBox.y; y < boundBox.y + boundBox.height; y++) { for (int x = boundBox.x; x < boundBox.x + boundBox.width; x++) { Tile tile = ((TileLayer) other).getTileAt(x, y); if (mask.contains(x, y) && tile != null) { setTileAt(x, y, tile); } } } } /** * {@inheritDoc} * * Copy data from another layer onto this layer. Unlike mergeOnto, * copyFrom() copies the empty cells as well. * @see MapLayer#mergeOnto */ @Override public void copyFrom(MapLayer other) { for (int y = bounds.y; y < bounds.y + bounds.height; y++) { for (int x = bounds.x; x < bounds.x + bounds.width; x++) { setTileAt(x, y, ((TileLayer) other).getTileAt(x, y)); } } } /** * {@inheritDoc} * * Like copyFrom, but will only copy the area specified. * @see TileLayer#copyFrom(MapLayer) */ @Override public void maskedCopyFrom(MapLayer other, Area mask) { Rectangle boundBox = mask.getBounds(); for (int y = boundBox.y; y < boundBox.y + boundBox.height; y++) { for (int x = boundBox.x; x < boundBox.x + boundBox.width; x++) { if (mask.contains(x, y)) { setTileAt(x, y, ((TileLayer) other).getTileAt(x, y)); } } } } /** * {@inheritDoc} * * Unlike mergeOnto, copyTo includes the null tile when merging. * @see MapLayer#copyFrom * @see MapLayer#mergeOnto * @see MapLayer#copyFrom * @see MapLayer#mergeOnto */ @Override public void copyTo(MapLayer other) { for (int y = bounds.y; y < bounds.y + bounds.height; y++) { for (int x = bounds.x; x < bounds.x + bounds.width; x++) { ((TileLayer) other).setTileAt(x, y, getTileAt(x, y)); } } } /** * {@inheritDoc} * * Creates a copy of this layer. * @see Object#clone */ @Override public Object clone() throws CloneNotSupportedException { TileLayer clone = (TileLayer) super.clone(); // Clone the layer data clone.map = new Tile[map.length][]; clone.tileInstanceProperties = new HashMap<>(); for (int i = 0; i < map.length; i++) { clone.map[i] = new Tile[map[i].length]; System.arraycopy(map[i], 0, clone.map[i], 0, map[i].length); for (int j = 0; j < map[i].length; j++) { Properties p = getTileInstancePropertiesAt(i, j); if (p != null) { Integer key = i + j * bounds.width; clone.tileInstanceProperties.put(key, (Properties) p.clone()); } } } return clone; } /** {@inheritDoc} */ @Override public void resize(int width, int height, int dx, int dy) { Tile[][] newMap = new Tile[height][width]; HashMap newTileInstanceProperties = new HashMap<>(); int maxX = Math.min(width, bounds.width + dx); int maxY = Math.min(height, bounds.height + dy); for (int x = Math.max(0, dx); x < maxX; x++) { for (int y = Math.max(0, dy); y < maxY; y++) { newMap[y][x] = getTileAt(x - dx, y - dy); Properties tip = getTileInstancePropertiesAt(x - dx, y - dy); if (tip != null) { newTileInstanceProperties.put(new Point(x, y), tip); } } } map = newMap; tileInstanceProperties = newTileInstanceProperties; bounds.width = width; bounds.height = height; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy