/*******************************************************************************
* Copyright (c) 2012-present Jakub Kováč, Jozef Brandýs, Katarína Kotrlová,
* Pavol Lukča, Ladislav Pápay, Viktor Tomkovič, Tatiana Tóthová
*
* 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 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 this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package algvis.core.visual;
import algvis.core.Node;
import algvis.core.history.HashtableStoreSupport;
import algvis.ui.view.View;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class Scene extends VisualElement {
public static final int MAXZ = 10, MIDZ = 5;
private final List<HashSet<VisualElement>> elements = new ArrayList<HashSet<VisualElement>>();
public final Set<VisualElement> elementsToRemove = new HashSet<VisualElement>();
private final List<VisualElement> temporaryElements = new ArrayList<VisualElement>();
public Scene() {
super(0);
for (int i = 0; i < MAXZ; ++i) {
elements.add(new HashSet<VisualElement>());
}
initRemoverThread();
}
private void initRemoverThread() {
// this thread waits until an element ends its animation and then
// removes it from the scene
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (Scene.this) {
// int poc = 0;
// for(Set<VisualElement> set : elements) poc +=
// set.size();
// System.out.println(elementsToRemove.size());
// System.out.println("elemSIZE: " + poc);
// System.out.println("elemSIZE2: " +
// elements.get(5).size());
final Iterator<VisualElement> iterator = elementsToRemove
.iterator();
while (iterator.hasNext()) {
final VisualElement element = iterator.next();
if (element.isAnimationDone()) {
iterator.remove();
if (element instanceof Node) {
// System.out.println("removed: " +
// ((Node) element).getKey());
// System.out.println("removed: " +
// element.getZDepth());
// System.out.println("removed: " +
// ((Node) element).state);
}
final Set<VisualElement> set = elements
.get(element.getZDepth());
set.remove(element);
}
}
}
synchronized (this) {
try {
wait(500);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
private Set<VisualElement> elementsAtDepth(int z) {
return elements.get(z);
}
public synchronized void add(VisualElement element) {
this.add(element, element.getZDepth());
}
public synchronized void add(VisualElement element, int zDepth) {
if (elementsToRemove.contains(element)) {
elementsToRemove.remove(element);
}
if (zDepth < 0) {
zDepth = 0;
}
if (zDepth >= MAXZ) {
zDepth = MAXZ - 1;
}
elementsAtDepth(zDepth).add(element);
}
public synchronized void addUntilNext(VisualElement element) {
this.add(element, element.getZDepth());
temporaryElements.add(element);
}
public synchronized void next() {
for (VisualElement e : temporaryElements) {
e.endAnimation();
elementsToRemove.add(e);
}
temporaryElements.clear();
}
/**
* Remove element after it ends its animation.
*
* @param element
*/
public synchronized void remove(VisualElement element) {
elementsToRemove.add(element);
}
/**
* Remove element immediately. Do not wait until it ends its animation.
*
* @param element
*/
public synchronized void removeNow(VisualElement element) {
elements.get(element.getZDepth()).remove(element);
if (elementsToRemove.contains(element)) {
elementsToRemove.remove(element);
}
}
/**
* Draw all elements on the scene.
*/
@Override
public void draw(View V) {
for (int i = MAXZ - 1; i >= 0; --i) {
synchronized (this) {
for (final VisualElement e : elementsAtDepth(i)) {
e.draw(V);
/*
* // testing bounding boxes
* Rectangle2D r = e.getBoundingBox();
* if (r != null) {
* V.setColor(Color.RED);
* V.drawRectangle(r);
* }
*/
}
}
}
}
/**
* Move all elements on the scene.
*/
@Override
public synchronized void move() {
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
e.move();
}
}
}
@Override
public synchronized Rectangle2D getBoundingBox() {
Rectangle2D retVal = null;
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
final Rectangle2D eBB = e.getBoundingBox();
if (retVal == null) {
retVal = eBB;
} else if (eBB != null) {
retVal = retVal.createUnion(eBB);
}
}
}
return retVal;
}
@Override
public synchronized void endAnimation() {
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
e.endAnimation();
}
}
}
@Override
public synchronized boolean isAnimationDone() {
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
if (!e.isAnimationDone()) {
return false;
}
}
}
return true;
}
@Override
public synchronized void storeState(Hashtable<Object, Object> state) {
/*
* TODO: the code below is soo uggly and inefficient...
* we would better like sth. like this:
* or store only the added/removed elements
* for (int i = 0; i < MAXZ; ++i) {
* for (final VisualElement element : elementsAtDepth(i)) {
* if (!elementsToRemove.contains(element)) {
* HashtableStoreSupport.store(state, hash + "elements" + i + "-" +
* element.hash,
* element);
* }
* element.storeState(state);
* }
* }
*/
final List<Set<VisualElement>> elementsClone = new ArrayList<Set<VisualElement>>();
for (final HashSet<VisualElement> set : elements) {
elementsClone.add((Set<VisualElement>) set.clone());
}
for (final VisualElement element : elementsToRemove) {
elementsClone.get(element.getZDepth()).remove(element);
}
for (int i = 0; i < MAXZ; ++i) {
HashtableStoreSupport.store(state, hash + "elements" + i,
elementsClone.get(i));
}
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
e.storeState(state);
}
}
}
@Override
public synchronized void restoreState(Hashtable<?, ?> state) {
for (int i = 0; i < MAXZ; ++i) {
final Set<VisualElement> setI = (Set<VisualElement>) state.get(hash
+ "elements" + i);
if (setI != null) {
for (final VisualElement e : elements.get(i)) {
if (setI.contains(e) && elementsToRemove.contains(e)) {
elementsToRemove.remove(e);
} else if (!setI.contains(e)
&& !elementsToRemove.contains(e)) {
remove(e);
}
}
for (final VisualElement e : setI) {
if (!elements.get(i).contains(e)) {
add(e, e.getZDepth());
}
}
}
}
for (final Set<VisualElement> set : elements) {
for (final VisualElement e : set) {
e.restoreState(state);
}
}
}
public synchronized void clear() {
for (final VisualElement e : elementsToRemove) {
elementsToRemove.remove(e);
}
for (final Set<VisualElement> set : elements) {
set.clear();
}
}
}