/*
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 java.awt.Cursor;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import javax.swing.SwingUtilities;
import org.gephi.visualization.GraphLimits;
import org.gephi.visualization.VizArchitecture;
import org.gephi.visualization.VizController;
import org.gephi.visualization.apiimpl.GraphContextMenu;
import org.gephi.visualization.apiimpl.GraphIO;
import org.gephi.visualization.apiimpl.VizEventManager;
import org.gephi.visualization.opengl.AbstractEngine;
import org.gephi.lib.gleem.linalg.MathUtil;
import org.gephi.lib.gleem.linalg.Vec3f;
import org.gephi.visualization.api.selection.SelectionManager;
import org.gephi.visualization.selection.Rectangle;
/**
*
* @author Mathieu Bastian
*/
public class StandardGraphIO implements GraphIO, VizArchitecture {
//Architecture
protected GraphDrawableImpl graphDrawable;
protected AbstractEngine engine;
protected VizEventManager vizEventManager;
protected VizController vizController;
protected GraphLimits limits;
//Listeners data
protected float[] rightButtonMoving = {-1f, 0f, 0f};
protected float[] leftButtonMoving = {-1f, 0f, 0f};
protected float[] middleButtonMoving = {-1f, 0f, 0f};
protected float[] mousePosition = new float[2];
protected float[] mouseDrag3d = new float[2];
protected float[] mouseDrag = new float[2];
protected float[] startDrag2d = new float[2];
//Flags
protected boolean draggingEnable = true;
protected boolean dragging = false;
protected boolean pressing = false;
@Override
public void initArchitecture() {
this.graphDrawable = VizController.getInstance().getDrawable();
this.engine = VizController.getInstance().getEngine();
this.vizEventManager = VizController.getInstance().getVizEventManager();
this.vizController = VizController.getInstance();
this.limits = VizController.getInstance().getLimits();
}
public void startMouseListening() {
stopMouseListening();
if (vizController.getVizConfig().isCameraControlEnable()) {
graphDrawable.graphComponent.addMouseListener(this);
graphDrawable.graphComponent.addMouseWheelListener(this);
}
if (vizController.getVizConfig().isSelectionEnable()) {
graphDrawable.graphComponent.addMouseMotionListener(this);
}
}
public void stopMouseListening() {
graphDrawable.graphComponent.removeMouseListener(this);
graphDrawable.graphComponent.removeMouseMotionListener(this);
graphDrawable.graphComponent.removeMouseWheelListener(this);
}
public void mousePressed(MouseEvent e) {
if (!graphDrawable.getGraphComponent().isShowing()) {
return;
}
float x = e.getLocationOnScreen().x - graphDrawable.graphComponent.getLocationOnScreen().x;
float y = e.getLocationOnScreen().y - graphDrawable.graphComponent.getLocationOnScreen().y;
if (SwingUtilities.isRightMouseButton(e)) {
//Save the coordinate of the start
rightButtonMoving[0] = x;
rightButtonMoving[1] = y;
graphDrawable.graphComponent.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
vizEventManager.mouseRightPress();
} else if (vizController.getVizModel().isRotatingEnable() && SwingUtilities.isMiddleMouseButton(e)) {
middleButtonMoving[0] = x;
middleButtonMoving[1] = y;
graphDrawable.graphComponent.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
vizEventManager.mouseMiddlePress();
} else if (SwingUtilities.isLeftMouseButton(e)) {
leftButtonMoving[0] = x;
leftButtonMoving[1] = y;
pressing = true;
vizEventManager.mouseLeftPress();
}
}
public void mouseReleased(MouseEvent e) {
//Disable the right button moving
rightButtonMoving[0] = -1;
leftButtonMoving[0] = -1;
middleButtonMoving[0] = -1;
//Update mouse position because the movement during dragging
if (graphDrawable.getGraphComponent().isShowing()) {
float x = e.getLocationOnScreen().x - graphDrawable.graphComponent.getLocationOnScreen().x;
float y = e.getLocationOnScreen().y - graphDrawable.graphComponent.getLocationOnScreen().y;
mousePosition[0] = x;
mousePosition[1] = graphDrawable.viewport.get(3) - y;
}
if (dragging) {
dragging = false;
engine.getScheduler().requireStopDrag();
vizEventManager.stopDrag();
} else {
graphDrawable.graphComponent.setCursor(Cursor.getDefaultCursor());
}
vizEventManager.mouseReleased();
if (pressing) {
pressing = false;
}
}
public void mouseEntered(MouseEvent e) {
dragging = false;
/*if (!engine.getScheduler().isAnimating()) {
engine.getScheduler().start();
}*/
}
public void mouseExited(MouseEvent e) {
if (!dragging) {
//engine.getScheduler().stop();
}
}
public void mouseMoved(MouseEvent e) {
if (!graphDrawable.getGraphComponent().isShowing()) {
return;
}
float x = e.getLocationOnScreen().x - graphDrawable.graphComponent.getLocationOnScreen().x;
float y = e.getLocationOnScreen().y - graphDrawable.graphComponent.getLocationOnScreen().y;
mousePosition[0] = x;
mousePosition[1] = graphDrawable.viewport.get(3) - y;
engine.getScheduler().requireUpdateSelection();
vizEventManager.mouseMove();
}
/**
* Mouse clicked event.
*/
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
if (vizController.getVizConfig().isSelectionEnable() && engine.isRectangleSelection()) {
Rectangle r = (Rectangle) engine.getCurrentSelectionArea();
boolean ctrl = (e.getModifiers() & InputEvent.CTRL_DOWN_MASK) != 0 || (e.getModifiers() & InputEvent.CTRL_MASK) != 0;
r.setCtrl(ctrl);
}
engine.getScheduler().requireMouseClick();
vizEventManager.mouseLeftClick();
} else if (SwingUtilities.isRightMouseButton(e)) {
if (vizController.getVizConfig().isContextMenu()) {
GraphContextMenu popupMenu = new GraphContextMenu();
popupMenu.getMenu().show(graphDrawable.getGraphComponent(), (int) mousePosition[0], (int) (graphDrawable.viewport.get(3) - mousePosition[1]));
}
vizEventManager.mouseRightClick();
} else if (SwingUtilities.isMiddleMouseButton(e)) {
vizEventManager.mouseMiddleClick();
}
}
public void mouseDragged(MouseEvent e) {
if (!graphDrawable.getGraphComponent().isShowing()) {
return;
}
float x = e.getLocationOnScreen().x - graphDrawable.graphComponent.getLocationOnScreen().x;//TODO Pourqoui ce osnt des float et pas des int
float y = e.getLocationOnScreen().y - graphDrawable.graphComponent.getLocationOnScreen().y;
if (rightButtonMoving[0] != -1) {
//The right button is pressed
float proche = graphDrawable.cameraTarget[2] - graphDrawable.cameraLocation[2];
proche = proche / 300;
graphDrawable.cameraTarget[0] += (x - rightButtonMoving[0]) * proche;
graphDrawable.cameraTarget[1] += (rightButtonMoving[1] - y) * proche;
graphDrawable.cameraLocation[0] += (x - rightButtonMoving[0]) * proche;
graphDrawable.cameraLocation[1] += (rightButtonMoving[1] - y) * proche;
rightButtonMoving[0] = x;
rightButtonMoving[1] = y;
engine.getScheduler().requireUpdateVisible();
}
if (middleButtonMoving[0] != -1) {
//The middle button is pressed
float angleY = y - middleButtonMoving[1];
if (angleY > 0 || (graphDrawable.cameraTarget[1] - graphDrawable.cameraLocation[1] > 0)) {
middleButtonMoving[1] = y;
graphDrawable.cameraLocation[1] = graphDrawable.cameraLocation[1] - Math.abs(graphDrawable.cameraLocation[2] - graphDrawable.cameraTarget[2]) * (float) Math.sin(Math.toRadians(angleY));
engine.getScheduler().requireUpdateVisible();
}
}
if (leftButtonMoving[0] != -1) {
//Remet à jour aussi la mousePosition pendant le drag, notamment pour coller quand drag released
mousePosition[0] = x;
mousePosition[1] = graphDrawable.viewport.get(3) - y;
mouseDrag3d[0] = (float) ((graphDrawable.viewport.get(2) / 2 - x) / graphDrawable.draggingMarker[0] + graphDrawable.cameraTarget[0]);
mouseDrag3d[1] = (float) ((y - graphDrawable.viewport.get(3) / 2) / graphDrawable.draggingMarker[1] + graphDrawable.cameraTarget[1]);
if (vizController.getVizConfig().isSelectionEnable() && engine.isRectangleSelection()) {
if (!dragging) {
//Start drag
dragging = true;
Rectangle rectangle = (Rectangle) engine.getCurrentSelectionArea();
rectangle.start(mousePosition);
}
engine.getScheduler().requireUpdateSelection();
} else if (vizController.getVizConfig().isDraggingEnable()) {
if (!dragging) {
//Start drag
dragging = true;
engine.getScheduler().requireStartDrag();
}
engine.getScheduler().requireDrag();
} else if (vizController.getVizConfig().isMouseSelectionUpdateWhileDragging()) {
engine.getScheduler().requireDrag();
} else {
if (!dragging) {
//Start drag
dragging = true;
startDrag2d[0] = x;
startDrag2d[1] = y;
vizEventManager.startDrag();
}
mouseDrag[0] = x - startDrag2d[0];
mouseDrag[1] = startDrag2d[1] - y;
vizEventManager.drag();
}
leftButtonMoving[0] = x;
leftButtonMoving[1] = y;
}
}
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getUnitsToScroll() == 0) {
return;
}
boolean ctrl = (e.getModifiers() & InputEvent.CTRL_DOWN_MASK) != 0 || (e.getModifiers() & InputEvent.CTRL_MASK) != 0;
if (ctrl) {
SelectionManager manager = VizController.getInstance().getSelectionManager();
if (!manager.isRectangleSelection()) {
int s = manager.getMouseSelectionDiameter();
s += -e.getUnitsToScroll() * 2;
s = Math.min(1000, s);
s = Math.max(1, s);
manager.setMouseSelectionDiameter(s);
}
return;
}
//Attributes
float way = -e.getUnitsToScroll() / Math.abs(e.getUnitsToScroll());
Vec3f cameraVector = graphDrawable.getCameraVector().copy();
float cameraLocation[] = graphDrawable.getCameraLocation();
float cameraTarget[] = graphDrawable.getCameraTarget();
//Distance
float distance = limits.getDistanceFromPoint(cameraLocation[0], cameraLocation[1], cameraLocation[2]);
float distanceRatio = MathUtil.clamp(2 * distance / 10000f, 0f, 2f);
float coeff = (float) (Math.exp(distanceRatio - 2) * 2.2 - 0.295); //exp(x-2)*2.2-0.3
float step = way * (10f + 1000 * coeff);
if (way == -1) {
step *= 3;
}
float stepRatio = step / distance;
//Get mouse position within the clipping plane
float mouseX = MathUtil.clamp(mousePosition[0], limits.getMinXviewport(), limits.getMaxXviewport());
float mouseY = MathUtil.clamp(mousePosition[1], limits.getMinYviewport(), limits.getMaxYviewport());
mouseX = mouseX - graphDrawable.viewport.get(2) / 2f; //Set to centric coordinates
mouseY = mouseY - graphDrawable.viewport.get(3) / 2f;
//Transform in 3d coordinates
mouseX /= -graphDrawable.draggingMarker[0];
mouseY /= -graphDrawable.draggingMarker[1];
//Set stepVector for zooming, direction of camera and norm of step
cameraVector.normalize();
Vec3f stepVec = cameraVector.times(step);
cameraLocation[0] += stepVec.x();
cameraLocation[1] += stepVec.y();
cameraLocation[2] += stepVec.z();
cameraLocation[2] = MathUtil.clamp(cameraLocation[2], 1f, Float.POSITIVE_INFINITY);
//System.out.println("camera: "+graphDrawable.cameraLocation[2]);
//Displacement of camera according to mouse position. Clamped to graph limits
Vec3f disVec = new Vec3f(mouseX, mouseY, 0);
disVec.scale(stepRatio);
//System.out.println(disVec.x()+" "+disVec.y()+" "+disVec.z());
cameraLocation[0] += disVec.x();
cameraLocation[1] += disVec.y();
cameraLocation[2] += disVec.z();
cameraTarget[0] += disVec.x();
cameraTarget[1] += disVec.y();
cameraTarget[2] += disVec.z();
//Refresh
engine.getScheduler().requireUpdateVisible();
//Too slow as it triggers many events later
//vizController.getVizModel().setCameraDistance(graphDrawable.getCameraVector().length());
/* float[] graphLimits = engine.getGraphLimits();
float graphWidth = Math.abs(graphLimits[1]-graphLimits[0]);
float graphHeight = Math.abs(graphLimits[3]-graphLimits[2]);
//On reduit l'hypothenuse et on calcule les depl z et y correpsondant
double hypotenuse = Math.sqrt(Math.pow(graphDrawable.cameraTarget[1] - graphDrawable.cameraLocation[1],2d) +
Math.pow(graphDrawable.cameraTarget[2] - graphDrawable.cameraLocation[2],2d));
float move = e.getUnitsToScroll()*((float)hypotenuse*0.05f);
float widthRatio = graphWidth/(float)hypotenuse;
float heightRatio = graphHeight/(float)hypotenuse;
float distanceRatio = Math.max(widthRatio, heightRatio);
if(e.getUnitsToScroll() > 0 && distanceRatio < 0.03f)
return;
if(hypotenuse + move > 2 ) {
hypotenuse = hypotenuse + move;
double disY = hypotenuse*Math.sin(graphDrawable.rotationX);
double disZ = hypotenuse*Math.cos(graphDrawable.rotationX);
float moveY = e.getUnitsToScroll()*(float)(disY*1/(8+distanceRatio));
float moveZ = e.getUnitsToScroll()*(float)(disZ*1/(8+distanceRatio));
//float moveY = e.getUnitsToScroll()*(float)(disY*0.05f);
//float moveZ = e.getUnitsToScroll()*(float)(disZ*0.05f);
graphDrawable.cameraLocation[1] += moveY;
graphDrawable.cameraLocation[2] += moveZ;
graphDrawable.rotationX = (float)Math.atan(((graphDrawable.cameraLocation[1]-graphDrawable.cameraTarget[1])/(graphDrawable.cameraLocation[2]-graphDrawable.cameraTarget[2])));
engine.getScheduler().requireUpdateVisible();
}*/
}
public void setCameraDistance(float distance) {
float cameraLocation[] = graphDrawable.getCameraLocation();
float cameraTarget[] = graphDrawable.getCameraTarget();
Vec3f camVect = new Vec3f(cameraTarget[0] - cameraLocation[0], cameraTarget[1] - cameraLocation[1], cameraTarget[2] - cameraLocation[2]);
float diff = camVect.length() - distance;
if (Math.abs(diff) > 1f) {
camVect.normalize();
cameraLocation[0] += camVect.x() * diff;
cameraLocation[1] += camVect.y() * diff;
cameraLocation[2] += camVect.z() * diff;
cameraLocation[2] = Math.max(0.5f, cameraLocation[2]);
engine.getScheduler().requireUpdateVisible();
}
}
public float[] getMousePosition3d() {
float[] m = new float[2];
m[0] = mousePosition[0] - graphDrawable.viewport.get(2) / 2f; //Set to centric coordinates
m[1] = mousePosition[1] - graphDrawable.viewport.get(3) / 2f;
m[0] /= -graphDrawable.draggingMarker[0]; //Transform in 3d coordinates
m[1] /= -graphDrawable.draggingMarker[1];
m[0] += graphDrawable.cameraTarget[0];
m[1] += graphDrawable.cameraTarget[1];
return m;
}
public void trigger() {
if (pressing) {
vizEventManager.mouseLeftPressing();
}
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public float[] getMousePosition() {
return mousePosition;
}
public float[] getMouseDrag() {
return mouseDrag;
}
public float[] getMouseDrag3d() {
return mouseDrag3d;
}
public void centerOnZero() {
graphDrawable.cameraLocation[0] = 0;
graphDrawable.cameraLocation[1] = 0;
graphDrawable.cameraLocation[2] = 100;
graphDrawable.cameraTarget[0] = 0;
graphDrawable.cameraTarget[1] = 0;
graphDrawable.cameraTarget[2] = 0;
//Refresh
engine.getScheduler().requireUpdateVisible();
}
public void centerOnGraph() {
float graphWidth = Math.abs(limits.getMaxXoctree() - limits.getMinXoctree());
float graphHeight = Math.abs(limits.getMaxYoctree() - limits.getMinYoctree());
float currentDistanceGraphRatioX = Math.abs(graphDrawable.viewport.get(2) / (float) graphDrawable.getDraggingMarkerX()) / graphDrawable.cameraLocation[2];
float currentDistanceGraphRatioY = Math.abs(graphDrawable.viewport.get(3) / (float) graphDrawable.getDraggingMarkerY()) / graphDrawable.cameraLocation[2];
float newCameraLocationX = graphWidth / currentDistanceGraphRatioX;
float newCameraLocationY = graphHeight / currentDistanceGraphRatioY;
float newCameraLocation = Math.max(newCameraLocationX, newCameraLocationY);
graphDrawable.cameraLocation[0] = limits.getMinXoctree() + graphWidth / 2;
graphDrawable.cameraLocation[1] = limits.getMinYoctree() + graphWidth / 2;
graphDrawable.cameraLocation[2] = newCameraLocation;
graphDrawable.cameraTarget[0] = graphDrawable.cameraLocation[0];
graphDrawable.cameraTarget[1] = graphDrawable.cameraLocation[1];
graphDrawable.cameraTarget[2] = 0;
//Refresh
engine.getScheduler().requireUpdateVisible();
}
public void centerOnCoordinate(float x, float y, float z) {
graphDrawable.cameraTarget[0] = x;
graphDrawable.cameraTarget[1] = y;
graphDrawable.cameraTarget[2] = z;
graphDrawable.cameraLocation[0] = x;
graphDrawable.cameraLocation[1] = y;
graphDrawable.cameraLocation[2] = z + 100;
}
}