/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. Copyright 2011 Gephi Consortium. All rights reserved. The contents of this file are subject to the terms of either the GNU General Public License Version 3 only ("GPL") or the Common Development and Distribution License("CDDL") (collectively, the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the License at http://gephi.org/about/legal/license-notice/ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the specific language governing permissions and limitations under the License. When distributing the software, include this License Header Notice in each file and include the License files at /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the License Header, with the fields enclosed by brackets [] replaced by your own identifying information: "Portions Copyrighted [year] [name of copyright owner]" If you wish your version of this file to be governed by only the CDDL or only the GPL Version 3, indicate your decision by adding "[Contributor] elects to include this software in this distribution under the [CDDL or GPL Version 3] license." If you do not indicate a single choice of license, a recipient has the option to distribute your version of this file under either the CDDL, the GPL Version 3 or to extend the choice of license to its licensees as provided above. However, if you add GPL Version 3 code and therefore, elected the GPL Version 3 license, then the option applies only if the new code is made subject to such option by the copyright holder. Contributor(s): Portions Copyrighted 2011 Gephi Consortium. */ 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); } }