/* 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.swing; import com.sun.opengl.util.BufferUtil; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.nio.DoubleBuffer; import java.nio.IntBuffer; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.glu.GLU; import org.gephi.visualization.VizArchitecture; import org.gephi.visualization.VizController; import org.gephi.visualization.apiimpl.GraphDrawable; import org.gephi.visualization.opengl.AbstractEngine; import org.gephi.lib.gleem.linalg.Vec3f; import org.gephi.visualization.apiimpl.Scheduler; /** * * @author Mathieu Bastian */ public class GraphDrawableImpl extends GLAbstractListener implements VizArchitecture, GraphDrawable { protected Component graphComponent; protected AbstractEngine engine; protected Scheduler scheduler; protected float[] cameraLocation; protected float[] cameraTarget; protected double[] draggingMarker = new double[2];//The drag mesure for a moving of 1 to the viewport protected Vec3f cameraVector = new Vec3f(); public GraphDrawableImpl() { super(); this.vizController = VizController.getInstance(); } public void initArchitecture() { this.engine = VizController.getInstance().getEngine(); this.scheduler = VizController.getInstance().getScheduler(); this.screenshotMaker = VizController.getInstance().getScreenshotMaker(); cameraLocation = vizController.getVizConfig().getDefaultCameraPosition(); cameraTarget = vizController.getVizConfig().getDefaultCameraTarget(); //Mouse events if (vizController.getVizConfig().isReduceFpsWhenMouseOut()) { final int minVal = vizController.getVizConfig().getReduceFpsWhenMouseOutValue(); final int maxVal = 30; graphComponent.addMouseListener(new MouseAdapter() { private float lastTarget = 0.1f; @Override public void mouseEntered(MouseEvent e) { if (!scheduler.isAnimating()) { engine.startDisplay(); } scheduler.setFps(maxVal); resetFpsAverage(); } @Override public void mouseExited(MouseEvent e) { float fps = getFpsAverage(); float target = (float) (fps / (1. / Math.sqrt(getFpsAverage()) * 10.)); if (fps == 0f) { target = lastTarget; } if (target <= 0.005f) { engine.stopDisplay(); } else if (target > minVal) { target = minVal; } lastTarget = target; scheduler.setFps(target); } }); } else if (vizController.getVizConfig().isPauseLoopWhenMouseOut()) { graphComponent.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { engine.startDisplay(); } @Override public void mouseExited(MouseEvent e) { engine.stopDisplay(); } }); } } @Override protected void init(GL gl) { //System.out.println("init"); graphComponent.setCursor(Cursor.getDefaultCursor()); engine.initEngine(gl, glu); } public void refreshDraggingMarker() { //Refresh dragging marker /*DoubleBuffer objPos = BufferUtil.newDoubleBuffer(3); glu.gluProject(0, 0, 0, modelMatrix, projMatrix, viewport, objPos); double dxx = objPos.get(0); double dyy = objPos.get(1); glu.gluProject(1, 1, 0, modelMatrix, projMatrix, viewport, objPos); draggingMarker[0] = dxx - objPos.get(0); draggingMarker[1] = dyy - objPos.get(1); System.out.print(draggingMarker[0]);*/ double[] v = {0, 0, 0, 1.0}; double[] v2 = {1.0, 1.0, 0, 1.0}; double[] d = myGluProject(v); double[] d2 = myGluProject(v2); draggingMarker[0] = d[0] - d2[0]; draggingMarker[1] = d[1] - d2[1]; } @Override public void setCameraPosition(GL gl, GLU glu) { //Refresh rotation angle gl.glLoadIdentity(); glu.gluLookAt(cameraLocation[0], cameraLocation[1], cameraLocation[2], cameraTarget[0], cameraTarget[1], cameraTarget[2], 0, 1, 0); gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelMatrix); cameraVector.set(cameraTarget[0] - cameraLocation[0], cameraTarget[1] - cameraLocation[1], cameraTarget[2] - cameraLocation[2]); refreshDraggingMarker(); } @Override protected void reshape3DScene(GL gl) { setCameraPosition(gl, glu); graphComponent.invalidate(); //Force canvas to be laid out with the proper size } @Override protected void render3DScene(GL gl, GLU glu) { scheduler.display(gl, glu); //renderTestCube(gl); } private void renderTestCube(GL gl) { float cubeSize = 100f; gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glLoadIdentity(); glu.gluLookAt(cameraLocation[0], cameraLocation[1], cameraLocation[2], cameraTarget[0], cameraTarget[1], cameraTarget[2], 0, 1, 0); gl.glColor3f(0f, 0f, 0f); gl.glRotatef(15.0f, 0.0f, 1.0f, 0.0f); // Rotate The cube around the Y axis gl.glRotatef(15.0f, 1.0f, 1.0f, 1.0f); gl.glBegin(GL.GL_QUADS); // Draw The Cube Using quads gl.glColor3f(0.0f, 1.0f, 0.0f); // Color Blue gl.glVertex3f(cubeSize, cubeSize, -cubeSize); // Top Right Of The Quad (Top) gl.glVertex3f(-cubeSize, cubeSize, -cubeSize); // Top Left Of The Quad (Top) gl.glVertex3f(-cubeSize, cubeSize, cubeSize); // Bottom Left Of The Quad (Top) gl.glVertex3f(cubeSize, cubeSize, 1.0f); // Bottom Right Of The Quad (Top) gl.glColor3f(1.0f, 0.5f, 0.0f); // Color Orange gl.glVertex3f(cubeSize, -cubeSize, cubeSize); // Top Right Of The Quad (Bottom) gl.glVertex3f(-cubeSize, -cubeSize, cubeSize); // Top Left Of The Quad (Bottom) gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize); // Bottom Left Of The Quad (Bottom) gl.glVertex3f(cubeSize, -cubeSize, -cubeSize); // Bottom Right Of The Quad (Bottom) gl.glColor3f(1.0f, 0.0f, 0.0f); // Color Red gl.glVertex3f(cubeSize, cubeSize, cubeSize); // Top Right Of The Quad (Front) gl.glVertex3f(-cubeSize, cubeSize, cubeSize); // Top Left Of The Quad (Front) gl.glVertex3f(-cubeSize, -cubeSize, cubeSize); // Bottom Left Of The Quad (Front) gl.glVertex3f(cubeSize, -cubeSize, cubeSize); // Bottom Right Of The Quad (Front) gl.glColor3f(1.0f, 1.0f, 0.0f); // Color Yellow gl.glVertex3f(cubeSize, -cubeSize, -cubeSize); // Top Right Of The Quad (Back) gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize); // Top Left Of The Quad (Back) gl.glVertex3f(-cubeSize, cubeSize, -cubeSize); // Bottom Left Of The Quad (Back) gl.glVertex3f(cubeSize, cubeSize, -cubeSize); // Bottom Right Of The Quad (Back) gl.glColor3f(0.0f, 0.0f, 1.0f); // Color Blue gl.glVertex3f(-cubeSize, cubeSize, cubeSize); // Top Right Of The Quad (Left) gl.glVertex3f(-cubeSize, cubeSize, -cubeSize); // Top Left Of The Quad (Left) gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize); // Bottom Left Of The Quad (Left) gl.glVertex3f(-cubeSize, -cubeSize, cubeSize); // Bottom Right Of The Quad (Left) gl.glColor3f(1.0f, 0.0f, 1.0f); // Color Violet gl.glVertex3f(cubeSize, cubeSize, -cubeSize); // Top Right Of The Quad (Right) gl.glVertex3f(cubeSize, cubeSize, cubeSize); // Top Left Of The Quad (Right) gl.glVertex3f(cubeSize, -cubeSize, cubeSize); // Bottom Left Of The Quad (Right) gl.glVertex3f(cubeSize, -cubeSize, -cubeSize); // Bottom Right Of The Quad (Right) gl.glEnd(); // End Drawing The Cube } public void renderScreenshot(GLAutoDrawable drawable) { GL gl = drawable.getGL(); if (vizController.getVizModel().isUse3d()) { gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); } else { gl.glClear(GL.GL_COLOR_BUFFER_BIT); } setCameraPosition(gl, glu); engine.display(gl, glu); } public void display() { drawable.display(); } //Utils public double[] myGluProject(float x, float y) { return myGluProject(new double[]{x, y, 0, 1.0}); } public double[] myGluProject(float x, float y, float z) { return myGluProject(new double[]{x, y, z, 1.0}); } public double[] myGluProject(double[] in) { double[] res = new double[2]; double[] out = transformVect(in, modelMatrix); double[] out2 = transformVect(out, projMatrix); out2[0] /= out2[3]; out2[1] /= out2[3]; out2[2] /= out2[3]; res[0] = viewport.get(0) + (out2[0] + 1) * viewport.get(2) / 2; res[1] = viewport.get(1) + viewport.get(3) * (out2[1] + 1) / 2; return res; } private double[] transformVect(double[] in, DoubleBuffer m) { double[] out = new double[4]; out[0] = m.get(0) * in[0] + m.get(4) * in[1] + m.get(8) * in[2] + m.get(12) * in[3]; out[1] = m.get(1) * in[0] + m.get(5) * in[1] + m.get(9) * in[2] + m.get(13) * in[3]; out[2] = m.get(2) * in[0] + m.get(6) * in[1] + m.get(10) * in[2] + m.get(14) * in[3]; out[3] = m.get(3) * in[0] + m.get(7) * in[1] + m.get(11) * in[2] + m.get(15) * in[3]; return out; } public double[] gluUnProject(float x, float y, float z) { DoubleBuffer buffer = BufferUtil.newDoubleBuffer(3); glu.gluUnProject(x, y, z, modelMatrix, projMatrix, viewport, buffer); return new double[]{buffer.get(0), buffer.get(1), buffer.get(2)}; } public float[] getCameraLocation() { return cameraLocation; } public void setCameraLocation(float[] cameraLocation) { this.cameraLocation = cameraLocation; } public float[] getCameraTarget() { return cameraTarget; } public void setCameraTarget(float[] cameraTarget) { this.cameraTarget = cameraTarget; } public Component getGraphComponent() { return graphComponent; } public Vec3f getCameraVector() { return cameraVector; } public int getViewportHeight() { return viewport.get(3); } public int getViewportWidth() { return viewport.get(2); } public double getDraggingMarkerX() { return draggingMarker[0]; } public double getDraggingMarkerY() { return draggingMarker[1]; } public DoubleBuffer getProjectionMatrix() { return projMatrix; } public DoubleBuffer getModelMatrix() { return modelMatrix; } public IntBuffer getViewport() { return viewport; } }