/* * Copyright (C) 2010 Markus Echterhoff <tam@edu.uni-klu.ac.at> * * This file is part of EvoPaint. * * EvoPaint 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 3 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 EvoPaint. If not, see <http://www.gnu.org/licenses/>. */ package evopaint.util.mapping; import evopaint.interfaces.IRandomNumberGenerator; import evopaint.util.logging.Logger; import java.util.AbstractCollection; import java.util.Iterator; /** * * @author Markus Echterhoff <tam@edu.uni-klu.ac.at> */ public class ParallaxMap<T> extends AbstractCollection<T> { private class ParallaxMapIterator<T> implements Iterator { T [] data; int i; public boolean hasNext() { if (i == data.length) { return false; } while (data[i] == null) { i++; if (i == data.length) { return false; } } return true; } public T next() { return data[i++]; } public void remove() { data[i] = null; } public ParallaxMapIterator(T [] array) { this.i = 0; this.data = array; } } private T [] data; protected int width; protected int height; private int numElements; @Override public int size() { return data == null ? 0 : data.length; } @Override public Iterator<T> iterator() { return new ParallaxMapIterator(data); } public int getWidth() { return width; } public int getHeight() { return height; } public void setData(Object [] data) { this.data = (T[])data; } public T [] getData() { return data; } public T getUnclamped(int i) { return data[i]; } public T get(int i) { return data[wrap(i, data.length)]; } public T get(int x, int y) { return data[wrap(y, height) * width + wrap(x, width)]; } public T get(AbsoluteCoordinate ac) { return data[wrap(ac.y, height) * width + wrap(ac.x, width)]; } public T get(AbsoluteCoordinate ac, RelativeCoordinate rc) { return data[wrap(ac.y + rc.y, height) * width + wrap(ac.x + rc.x, width)]; } /* * * gets absolute index of a random free spot in the neighborhood of ac * the returned index will be wrapped to lie within the bounds of this * parallax map. * * @return a new AbsoluteCoordinate, pointing to the free spot */ public AbsoluteCoordinate getRandomFreeNeighborCoordinateOf(AbsoluteCoordinate ac, IRandomNumberGenerator rng) { // get number of free spots in neighborhood int numFree = 0; for (int y = ac.y - 1; y <= ac.y + 1; y++) { for (int x = ac.x - 1; x <= ac.x + 1; x++) { if (get(x, y) == null) { numFree++; } } } // lazy way out if we have no free spots if (numFree == 0) { return null; } // gather indices of free spots int [] indices = new int[numFree]; int i = 0; for (int y = ac.y - 1; y <= ac.y + 1; y++) { for (int x = ac.x - 1; x <= ac.x + 1; x++) { if (get(x, y) == null) { indices[i] = wrap(y, height) * width + wrap(x, width); i++; if (i == numFree) { int chosenIndex = indices[rng.nextPositiveInt(indices.length)]; return new AbsoluteCoordinate(chosenIndex % width, chosenIndex / width, this); } } } } assert (false); return null; } public int [] getShuffledIndices(IRandomNumberGenerator rng) { int [] indices = new int[numElements]; for (int i = 0, ii = 0; ii < numElements && i < data.length; i++) { if (data[i] != null) { indices[ii++] = i; } } // Durstenfeld's algorithm for permutating // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle // n is the number of items remaining to be shuffled. for (int n = indices.length; n > 1; n--) { // Pick a random element to swap with the nth element. int k = rng.nextPositiveInt(n); // 0 <= k <= n-1 (0-based array) // Swap array elements. int tmp = indices[k]; indices[k] = indices[n-1]; indices[n-1] = tmp; } return indices; } public T getRandom(IRandomNumberGenerator rng) { int rnd = rng.nextPositiveInt(data.length); int i = rnd; while (data[i] == null) { i++; if (i == data.length) { i = 0; } if (i == rnd) { Logger.log.warning("calling getRandom() on empty ParallaxMap", (Object[])null); } } return data[i]; } public void set(AbsoluteCoordinate ac, T object) { int i = wrap(ac.y, height) * width + wrap(ac.x, width); set(i, object); } public void set(int i, T object) { if (data[i] == null) { if (object != null) { numElements++; } } else if (object == null) { numElements--; } data[i] = object; } protected void set(int x, int y, T object) { int i = wrap(y, height) * width + wrap(x, width); set(i, object); } public void remove(AbsoluteCoordinate ac) { int i = wrap(ac.y, height) * width + wrap(ac.x, width); set(i, null); } public void remove(int x, int y) { int i = wrap(y, height) * width + wrap(x, width); set(i, null); } @Override public boolean remove(Object o) { return false; } public static int wrap(int index, int length) { while (index < 0) { index += length; } while (index >= length) { index -= length; } return index; } public void reset() { for (int i = 0; i < data.length; i++) { data[i] = null; } numElements = 0; } public void recount() { numElements = 0; for (int i = 0; i < data.length; i++) { if (data[i] != null) { numElements++; } } } public ParallaxMap(T[] array, int width, int height) { assert (array != null); assert (width > 0); assert (height > 0); this.data = array; this.width = width; this.height = height; } }