/* 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; } }