/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.visualization.opengl.octree; import com.sun.opengl.util.GLUT; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.media.opengl.GL; import javax.media.opengl.glu.GLU; import org.gephi.utils.collection.avl.AVLItemAccessor; import org.gephi.utils.collection.avl.ParamAVLTree; import org.gephi.utils.collection.avl.AVLItem; import org.gephi.visualization.apiimpl.ModelImpl; /** * * @author Mathieu Bastian */ public class Octant implements AVLItem { //Static private static int OctantIDs = 0; //Octree private Octree octree; //Coordinates private float size; private float posX; private float posY; private float posZ; private int depth; //Attributes private final int octantID; private int objectsCount = 0; private Octant[] children; private AtomicBoolean updateFlag = new AtomicBoolean(); //Models private List<ParamAVLTree<ModelImpl>> modelClasses; public Octant(Octree octree, int depth, float posX, float posY, float posZ, float size) { this.size = size; this.posX = posX; this.posY = posY; this.posZ = posZ; this.depth = depth; this.octree = octree; this.octantID = OctantIDs++; } public void addObject(int classID, ModelImpl obj) { if (children == null && depth < octree.getMaxDepth()) { //Create children subdivide(); } if (depth == octree.getMaxDepth()) { //First item add - Initialize if (objectsCount == 0) { octree.addLeaf(this); modelClasses = new ArrayList<ParamAVLTree<ModelImpl>>(octree.getClassesCount()); for (int i = 0; i < octree.getClassesCount(); i++) { modelClasses.add(new ParamAVLTree<ModelImpl>(new AVLItemAccessor<ModelImpl>() { public int getNumber(ModelImpl item) { return item.getNumber(); } })); } } //Get the list ParamAVLTree<ModelImpl> objectClass = this.modelClasses.get(classID); //Set Octant obj.setOctant(this); //Add at this node obj.setID(octree.getNextObjectID()); if (objectClass.add(obj)) { objectsCount++; } } else { if (classID == 0 && this == octree.root) { //Clamp Hack to avoid nodes to be outside octree float quantum = size / 2; float x = obj.getObj().x(); float y = obj.getObj().y(); float z = obj.getObj().z(); if (x > posX + quantum) { obj.getObj().setX(posX + quantum); } else if (x < posX - quantum) { obj.getObj().setX(posX - quantum); } if (y > posY + quantum) { obj.getObj().setY(posY + quantum); } else if (y < posY - quantum) { obj.getObj().setY(posY - quantum); } if (z > posZ + quantum) { obj.getObj().setZ(posZ + quantum); } else if (z < posZ - quantum) { obj.getObj().setZ(posZ - quantum); } } for (int index : obj.octreePosition(posX, posY, posZ, size)) { children[index].addObject(classID, obj); } } } public void removeObject(int classID, ModelImpl obj) { //Get the list ParamAVLTree<ModelImpl> objectClass = this.modelClasses.get(classID); if (objectClass.remove(obj)) { objectsCount--; } if (objectsCount == 0) { //Remove leaf octree.removeLeaf(this); } } public void clear(int classID) { ParamAVLTree<ModelImpl> tree = getTree(classID); int count = tree.getCount(); tree.clear(); objectsCount -= count; if (objectsCount == 0) { //Remove leaf octree.removeLeaf(this); } } public void subdivide() { float quantum = size / 4; float newSize = size / 2; Octant o1 = new Octant(octree, depth + 1, posX + quantum, posY + quantum, posZ - quantum, newSize); Octant o2 = new Octant(octree, depth + 1, posX - quantum, posY + quantum, posZ - quantum, newSize); Octant o3 = new Octant(octree, depth + 1, posX + quantum, posY + quantum, posZ + quantum, newSize); Octant o4 = new Octant(octree, depth + 1, posX - quantum, posY + quantum, posZ + quantum, newSize); Octant o5 = new Octant(octree, depth + 1, posX + quantum, posY - quantum, posZ - quantum, newSize); Octant o6 = new Octant(octree, depth + 1, posX - quantum, posY - quantum, posZ - quantum, newSize); Octant o7 = new Octant(octree, depth + 1, posX + quantum, posY - quantum, posZ + quantum, newSize); Octant o8 = new Octant(octree, depth + 1, posX - quantum, posY - quantum, posZ + quantum, newSize); children = new Octant[]{o1, o2, o3, o4, o5, o6, o7, o8}; } public Iterator<ModelImpl> iterator(int classID) { return this.modelClasses.get(classID).iterator(); } public ParamAVLTree<ModelImpl> getTree(int classID) { return modelClasses.get(classID); } public void displayOctant(GL gl) { /*if(children==null && depth==octree.getMaxDepth() && objectsCount>0) {*/ float quantum = size / 2; gl.glBegin(GL.GL_QUAD_STRIP); gl.glVertex3f(posX + quantum, posY + quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY - quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY + quantum, posZ - quantum); gl.glVertex3f(posX + quantum, posY - quantum, posZ - quantum); gl.glVertex3f(posX - quantum, posY + quantum, posZ - quantum); gl.glVertex3f(posX - quantum, posY - quantum, posZ - quantum); gl.glVertex3f(posX - quantum, posY + quantum, posZ + quantum); gl.glVertex3f(posX - quantum, posY - quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY + quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY - quantum, posZ + quantum); gl.glEnd(); gl.glBegin(GL.GL_QUADS); gl.glVertex3f(posX - quantum, posY + quantum, posZ - quantum); gl.glVertex3f(posX - quantum, posY + quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY + quantum, posZ + quantum); gl.glVertex3f(posX + quantum, posY + quantum, posZ - quantum); gl.glVertex3f(posX - quantum, posY - quantum, posZ + quantum); gl.glVertex3f(posX - quantum, posY - quantum, posZ - quantum); gl.glVertex3f(posX + quantum, posY - quantum, posZ - quantum); gl.glVertex3f(posX + quantum, posY - quantum, posZ + quantum); gl.glEnd(); /*} else if(children!=null) { for(Octant o : children) { o.displayOctreeNode(gl); } }*/ } public void displayOctantInfo(GL gl, GLU glu) { GLUT glut = new GLUT(); float quantum = size / 2; float height = 15; gl.glPushMatrix(); gl.glTranslatef(posX - quantum, posY + quantum - height, posZ + quantum); gl.glScalef(0.1f, 0.1f, 0.1f); gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glut.glutStrokeString(GLUT.STROKE_MONO_ROMAN, "ID: " + octantID); gl.glPopMatrix(); height += 15; gl.glPushMatrix(); gl.glTranslatef(posX - quantum, posY + quantum - height, posZ + quantum); gl.glScalef(0.1f, 0.1f, 0.1f); gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glut.glutStrokeString(GLUT.STROKE_MONO_ROMAN, "objectsCount: " + objectsCount); gl.glPopMatrix(); int i = 0; for (ParamAVLTree<ModelImpl> p : modelClasses) { height += 15; gl.glPushMatrix(); gl.glTranslatef(posX - quantum, posY + quantum - height, posZ + quantum); gl.glScalef(0.1f, 0.1f, 0.1f); gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f); glut.glutStrokeString(GLUT.STROKE_MONO_ROMAN, "class" + (i++) + ": " + p.getCount()); gl.glPopMatrix(); } } public int getNumber() { return octantID; } public float getPosX() { return posX; } public float getPosY() { return posY; } public float getPosZ() { return posZ; } public float getSize() { return size; } public boolean isRequiringUpdatePosition() { return updateFlag.get(); } public void requireUpdatePosition() { if (!updateFlag.getAndSet(true)) { octree.vizController.getScheduler().requireUpdatePosition(); } } public void resetUpdatePositionFlag() { updateFlag.set(false); } }