Package com.google.devtools.depan.eclipse.visualization.ogl

Source Code of com.google.devtools.depan.eclipse.visualization.ogl.GLScene

/*
* Copyright 2008 Yohann R. Coppel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.devtools.depan.eclipse.visualization.ogl;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.nio.IntBuffer;
import java.util.logging.Logger;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLProfile;
import javax.media.opengl.glu.GLU;

/**
* An abstract GLScene. Handling OpenGL specific details.
*
* @author Yohann Coppel
*/
public abstract class GLScene {
  public static final float FACTOR = 1f;

  private static final int[] EMPTY_HIT_LIST = new int[0];

  private static final Logger logger =
      Logger.getLogger(GLScene.class.getName());

  // For OSX, get profile early
  private static GLProfile DEFAULT_PROFILE = GLProfile.getDefault();

  private GLCanvas canvas;
  private final GLContext context;
  public final GL2 gl;
  public final GLU glu;
  private final SceneGrip grip;

  /** eye position */
  private float xoff;
  private float yoff;
  private float zoff;

  private float targetXoff;
  private float targetYoff;
  private float targetZoff;

  /** eye direction */
  private float xrot;
  private float yrot;
  private float zrot;

  private float targetXrot;
  private float targetYrot;
  private float targetZrot;

  private boolean isSceneChanged = false;

  /** speed for moves */
  public static final int SPEED = 5;

  /** Camera z position, if zoom value is set to "100%". */
  public static final float HUNDRED_PERCENT_ZOOM = 2000.0f;

  public static final float[] DEFAULT_CAMERA_POSITION = {
    0.0f, 0.0f, HUNDRED_PERCENT_ZOOM
  };

  // Full laptop screen vertical space:
  // 7" vertical from 22" is ~ 20 degrees.
  public static final float FOV = 20.0f;

  public static final float Z_NEAR = 0.4f;

  public static final float Z_FAR = 30000.0f;

  public static final float PIXEL_QUANTA = 0.1f;

  public static final float ZOOM_QUANTA = 1.0f;

  public static final float ZOOM_MAX = 1.1f;

  public static final float ROTATE_QUANTA = 0.001f;
 
  public static boolean hyperbolic = false;

  /** Latest received mouse positions. */
  private int mouseX, mouseY;

  /** Latest mouse coordinate when rectangle selection started */
  private boolean drawSelectRectangle = false;
  private int startSelectX, startSelectY;
  private int selectionWidth, selectionHeight;

  private Color foregroundColor = Color.WHITE;

  /**
   * Turn off SWT-display wheel listening for the canvas.
   * OGL rendering uses a separate mouse listening mechanism, and SWT
   * generates conflicting scroll-bar move events.
   */
  private final Listener wheelListener = new Listener() {
    @Override
    public void handleEvent(Event event) {
      if (event.widget != canvas) {
        return;
      }
      event.doit = false;
    }
  };

  public GLScene(Composite parent) {
    GLData data = new GLData();
    data.doubleBuffer = true;
    data.depthSize = 16;
    canvas = new GLCanvas(parent, SWT.NO_BACKGROUND, data);
    canvas.setCurrent();

    context = createGLContext();

    GL rawGL = context.getGL();
    if (rawGL.isGL2()) {
      gl = (GL2) rawGL;
    } else {
      gl = null;
    }
    glu = new GLU();

    canvas.addControlListener(new ControlAdapter() {
      @Override
      public void controlResized(ControlEvent e) {
        resizeScene();
      }
    });
    canvas.addDisposeListener(new DisposeListener() {
      @Override
      public void widgetDisposed(DisposeEvent e) {
        dispose();
      }
    });

    grip  = new SceneGrip(this);

    canvas.addMouseListener(grip);
    canvas.addMouseMoveListener(grip);
    canvas.addKeyListener(grip);
    canvas.addMouseWheelListener(grip);

    // Disable display support for mouse-wheel.
    Display display = canvas.getDisplay();
    display.addFilter(SWT.MouseVerticalWheel, wheelListener);
    display.addFilter(SWT.MouseHorizontalWheel, wheelListener);

    // Start with the drawing properly zoomed.
    homeCamera();
    cutCamera();

    this.init();
  }

  private GLContext createGLContext() {

    try {
      logger.info("Create context...");
      GLDrawableFactory drawableFactory = GLDrawableFactory.getFactory(DEFAULT_PROFILE);
      GLContext result = drawableFactory.createExternalGLContext();
      logger.info("    Done.");

      return result;
    } catch (Throwable errGl) {
      errGl.printStackTrace();
      throw new RuntimeException(errGl);
    }
  }

  protected void resizeScene() {
    canvas.setCurrent();
    context.makeCurrent();

    Rectangle rect = canvas.getClientArea();
    gl.glViewport(0, 0, rect.width, rect.height);

    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    updateViewpoint(rect);

    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();

    context.release();
  }

  private void updateViewpoint(Rectangle rect) {
    int width = rect.width;
    int height = Math.max(rect.height, 1);
    float aspect = (float) width / (float) height;

    glu.gluPerspective(FOV, aspect, Z_NEAR, Z_FAR);
  }

  protected void dispose() {
    if (canvas != null) {

      Display display = canvas.getDisplay();
      display.removeFilter(SWT.MouseVerticalWheel, wheelListener);
      display.removeFilter(SWT.MouseHorizontalWheel, wheelListener);

      canvas.dispose();
      canvas = null;
    }
  }

  protected void init() {
    initGLContext();
    initGL();
  }

  protected void initGLContext() {
    canvas.setCurrent();
  }

  protected void initGL() {
    context.makeCurrent();
    /*
    //TODO: add options for this parameters: it looks way nicer,
    //but is way slower ;)
    gl.glEnable(GL.GL_LINE_SMOOTH);
    gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
    gl.glEnable(GL.GL_POLYGON_SMOOTH);
    gl.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST);
    */
    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    gl.glClearDepth(1.0f);
    gl.glEnable(GL2.GL_DEPTH_TEST);
    gl.glEnable(GL2.GL_BLEND);
    gl.glEnable(GL2.GL_TEXTURE_2D);
    gl.glDepthFunc(GL2.GL_LEQUAL);
    gl.glShadeModel(GL2.GL_SMOOTH);
    gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
    gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
    context.release();
  }

  public void prepareResources() {
    context.makeCurrent();
    allocateResources();
    context.release();
  }

  /**
   * Hook method for derived classes to allocate resources within the
   * GLContenxt
   */
  protected void allocateResources() {
  }

  /**
   * @param elapsedTime time since previous frame.
   */
  public void render(float elapsedTime) {
    if (!canvas.isCurrent()) {
      canvas.setCurrent();
    }
    context.makeCurrent();
    gl.glPushMatrix();
    drawScene(elapsedTime);
    gl.glPopMatrix();
    if (drawSelectRectangle) {
      drawRectangle();
    }
    canvas.swapBuffers();
    context.release();
  }

  private int[] renderWithPicking() {
    if (!canvas.isCurrent()) {
      canvas.setCurrent();
    }
    context.makeCurrent();

    int[] viewPort = new int[4];
    gl.glGetIntegerv(GL2.GL_VIEWPORT, viewPort, 0);

    IntBuffer selectBuffer = getSelectBuffer();
    gl.glSelectBuffer(selectBuffer.capacity(), selectBuffer);

    // setup the view
    gl.glRenderMode(GL2.GL_SELECT);
    gl.glInitNames();

    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glPushMatrix();
    gl.glLoadIdentity();

    glu.gluPickMatrix(mouseX, viewPort[3] - mouseY, selectionWidth,
        selectionHeight, viewPort, 0);
    updateViewpoint(canvas.getClientArea());

    drawScene(0f);

    gl.glPopMatrix();
    gl.glFlush();
    int hits = gl.glRenderMode(GL2.GL_RENDER);
    gl.glMatrixMode(GL2.GL_MODELVIEW);

    context.release();
    return processHits(hits, selectBuffer);
  }

  /////////////////////////////////////
  // Camera (and drawing) management

  /**
   * Provide the coordinates for the camera.  These are the OGL coordinates
   * at which the camera is placed.  In rendering terms, that's the negative
   * of the (x,y) translation transform.
   *
   * @return the camera (eye) position.
   */
  public float[] getCameraPosition() {
    return new float[] {-xoff, -yoff, zoff};
  }

  public void homeCamera() {
    targetXrot = 0.0f;
    targetYoff = 0.0f;
    targetZrot = 0.0f;

    moveToCamera(0.0f, 0.0f);
    zoomToCamera(HUNDRED_PERCENT_ZOOM);
  }

  /**
   * Center the camera over the given point (in openGL coordinates)
   *
   * @param camX x coordinate
   * @param camY y coordinate
   */
  public void moveToCamera(float camX, float camY) {
    targetXoff = -camX;
    targetYoff = -camY;
    isSceneChanged = true;
  }

  /**
   * Avoid moving the camera too far or too close. so the scene
   * stays within the frustum for the perspective transform.
   *
   * No direct assignments to targetZoff.
   */
  public void zoomToCamera(float zOffset) {
    if (zOffset > (GLScene.Z_FAR - 1.0)) {
      zOffset = GLScene.Z_FAR - 1.0f;
      logger.info("clamped zoom at " + zOffset);
    }
    if (zOffset < (ZOOM_MAX)) {
      zOffset = ZOOM_MAX;
      logger.info("clamped zoom at " + zOffset);
    }

    targetZoff = zOffset;
    isSceneChanged = true;
  }

  public boolean isNowStable() {
    // With no changes,
    if (!isSceneChanged)
      return false;

    if (xoff != targetXoff)
      return false;
    if (yoff != targetYoff)
      return false;
    if (zoff != targetZoff)
      return false;

    clearChanges();
    return true;
  }

  public void clearChanges() {
    isSceneChanged = false;
  }

  /**
   * Set the zoom to the given value. 1.0 is 100%.
   * @param scale
   */
  public void setZoom(float scale) {
    zoomToCamera(HUNDRED_PERCENT_ZOOM / scale);
  }

  /**
   * Set the zoom to the given value. 1.0 is 100%.
   * @param scale
   */
  public void zoomCamera(float size) {
    zoomToCamera(targetZoff += size);
  }

  /**
   * Move the eye in straight line toward the given world position, reducing
   * (or augmenting) the distance between eye and point of zoomValue.
   *
   * @param x
   * @param y
   * @param z
   * @param zoomValue
   */
  public void zoomAt(double x, double y, double z, double zoomValue) {
    double[] diff = {targetXoff - x, targetYoff - y, z - targetZoff};
    double length = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]
        + diff[2] * diff[2]);
    if (length == 0) {
      length = 1;
    }
    double[] normalized = {diff[0] / length, diff[1] / length,
        diff[2] / length};
    // calculate zoom such that the difference on the Z axis is equal to
    // zoomValue
    double percent = zoomValue / normalized[2];

    moveToCamera(
        - (float) (targetXoff + normalized[0] * percent),
        - (float) (targetYoff + normalized[1] * percent));
    zoomToCamera((float) (targetZoff + normalized[2] * percent));
  }

  /**
   * Change direction of camera (pan -u/d, scan - l/r, or turn - c/cc).
   *
   * TODO: Make sure these match their behaviors
   * @param xRot - amount to tilt up or down
   * @param yRot - amount to pan left or right
   * @param zRot - amount to turn/twist clockwise or counterclockwise.
   */
  public void rotateCamera(float xRot, float yRot, float zRot) {
    targetXrot += xRot;
    targetYrot += yRot;
    targetZrot += zRot;
  }

  /**
   * Change camera position vertically (up or down)
   */
  public void pedestalCamera(float size) {
    moveToCamera(
        - (float) (targetXoff + (size * Math.sin(Math.toRadians(zrot)))),
        - (float) (targetYoff + (size * Math.cos(Math.toRadians(zrot)))));
  }

  /**
   * Change camera position horizontally (left or right).
   */
  public void truckCamera(float size) {
    // TODO:  Isn't the -90 just swapping sin/cos?
    moveToCamera(
        - (float) (targetXoff + (size * Math.sin(Math.toRadians(zrot - 90)))),
        - (float) (targetYoff + (size * Math.cos(Math.toRadians(zrot - 90)))));
  }

  /**
   * Cut the camera immediately to the target location.  No animation.
   */
  public void cutCamera() {
    xrot = targetXrot;
    yoff = targetYoff;
    zrot = targetZrot;

    xoff = targetXoff;
    yoff = targetYoff;
    zoff = targetZoff;
  }

  /**
   * Perform a step: move the camera if necessary
   */
  private void step() {
    if (isPixelEpsilon(xoff, targetXoff)) {
      xoff = targetXoff;
    } else {
      xoff += (targetXoff - xoff) / SPEED;
    }
    if (isPixelEpsilon(yoff, targetYoff)) {
      yoff = targetYoff;
    } else {
      yoff += (targetYoff - yoff) / SPEED;
    }

    if (isZoomEpsilon(zoff, targetZoff)) {
      zoff = targetZoff;
    } else {
      zoff += (targetZoff - zoff) / SPEED;
    }

    if (isRotateEpsilon(xrot, targetXrot)) {
      xrot = targetXrot;
    } else {
      xrot += (targetXrot - xrot) / SPEED;
    }
    if (isRotateEpsilon(yrot, targetYrot)) {
      yrot = targetYrot;
    } else {
      yrot += (targetYrot - yrot) / SPEED;
    }
    if (isRotateEpsilon(zrot, targetZrot)) {
      zrot = targetZrot;
    } else {
      zrot += (targetZrot - zrot) / SPEED;
    }
  }

  private boolean isPixelEpsilon(float left, float right) {
    return isEpsilon(left, right, PIXEL_QUANTA);
  }

  private boolean isZoomEpsilon(float left, float right) {
    return isEpsilon(left, right, ZOOM_QUANTA);
  }

  private boolean isRotateEpsilon(float left, float right) {
    return isEpsilon(left, right, ROTATE_QUANTA);
  }
 
  private boolean isEpsilon(float left, float right, float epsilon) {
    return Math.abs(left - right) < epsilon;
  }

  /**
   * Establish the position and direction of the camera.
   */
  private void prepareCamera() {
    step();

    if (!GLScene.hyperbolic) {
      gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);
      gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);
      gl.glTranslatef(xoff, yoff, -zoff);
    } else {
      glu.gluLookAt(0, 0, zoff, 0, 0, zoff - 1, 0, 1, 0);
    }
    //gl.glRotatef(this.xrot, 1.0f, 0.0f, 0.0f);
    //gl.glRotatef(this.yrot, 0.0f, 1.0f, 0.0f);
  }

  /////////////////////////////////////
  // Draw the scene

  /**
   * Clear the scene and adjust the camera position.
   *
   * Hook method for derived types to render the image.  Extending types
   * should call the super-method first to establish a clear image and
   * the proper grip.
   *
   * @param elapsedTime time since previous frame.
   */
  protected void drawScene(float elapsedTime) {
    gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
    prepareCamera();
  }

  public void printMousePos(int x, int y) {
    double[] m = getOGLPos(x, y);
    logger.info("Mouse " + m[0] + " : " + m[1] + " - " + m[2]);
  }

  /**
   * Convert screen coordinates (x, y) to OGL coordinates.
   *
   * In screen coordinates, y increases from top to bottom.
   * Screen coordinates are often pixels from a boundary.
   * In OGL coordinates, y increases from bottom to top.
   *
   * @param x screen x coordinate
   * @param y screen x coordinate
   * @return corresponding OGL coordinate for current view
   */
  public double[] getOGLPos(int x, int y) {
    int[] viewport = new int[4];
    double[] modelview = new double[16];
    double[] projection = new double[16];
    double[] wcoord0 = new double[3];
    double[] wcoord1 = new double[3];

    gl.glGetDoublev(GL2.GL_MODELVIEW_MATRIX, modelview, 0);
    gl.glGetDoublev(GL2.GL_PROJECTION_MATRIX, projection, 0);
    gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0);

    double winX = x;
    double winY = (double) viewport[3] - (double) y;

    // UnProject twice, once with z = 0 (zNear), and once with
    // z = 1 (zFar).
    glu.gluUnProject(winX, winY, 0,
        modelview, 0, projection, 0, viewport, 0, wcoord0, 0);
    glu.gluUnProject(winX, winY, 1.0,
        modelview, 0, projection, 0, viewport, 0, wcoord1, 0);

    // compute the vector between the two results.
    double[] vector = {wcoord1[0] - wcoord0[0], wcoord1[1] - wcoord0[1],
        wcoord1[2] - wcoord0[2]};
    // normalize it
    double[] norm = {vector[0] / vector[2], vector[1] / vector[2], 1.0f};
    // then we have 1 point (the camera), and one vector.
    // we can therefore compute the position of the point where
    // z = 0, for the line passing by the camera position, and
    // directed by the vector.
    double[] res = {-xoff + (-zoff) * norm[0],
        -yoff + (-zoff) * norm[1], 0f};
    return res;
  }

  public int[] getViewport() {
    int[] viewPort = new int[4];
    gl.glGetIntegerv(GL2.GL_VIEWPORT, viewPort, 0);
    return viewPort;
  }

  public Rectangle2D getOGLViewport() {
    int[] osViewport = getViewport();
    double[] bottomLeft = getOGLPos(osViewport[0], osViewport[3] - osViewport[1]);
    double[] topRight = getOGLPos(osViewport[0] + osViewport[2], 0);
    return new Rectangle2D.Double(
        bottomLeft[0], bottomLeft[1] /* left, bottom */ ,
        topRight[0] - bottomLeft[0] /* width */,
        topRight[1] - bottomLeft[1] /* height */ );
  }

  /**
   * Provide an IntBuffer that can be used to select from the current scene.
   * It should have enough room to allow every OGL entity in the scene to
   * be selected.
   * <p>
   * Ownership of the buffer remains with the provider (e.g. derived class),
   * and access is thread-safe.  But it should be a good place to dump the OGL
   * selection data.
   *
   * @return IntBuffer to collect OGL selection data
   */
  protected abstract IntBuffer getSelectBuffer();

  protected abstract boolean isSelected(int id);

  /**
   * Activate selection rendering for the exactly the listed nodes.
   */
  protected abstract void setSelection(int[] ids);

  /**
   * Turn on selection rendering for the listed nodes.
   */
  protected abstract void extendSelection(int[] ids);

  /**
   * Turn off selection rendering for the listed nodes.
   */
  protected abstract void reduceSelection(int[] ids);

  /**
   * Move every selected objects relatively. Movement is given relatively
   * to the openGL coordinates.
   *
   * @param dx movement on x axis.
   * @param dy movement on y axis.
   */
  protected abstract void moveSelectionDelta(double dx, double dy);

  /**
   * Move every selected objects relatively. Movement is given relatively
   * to window coordinates.
   *
   * @param x movement on x axis.
   * @param y movement on y axis.
   */
  public void moveSelectedObjectsTo(int x, int y) {
    double[] worldPosOrigin = getOGLPos(0, 0);
    double[] worldPos = getOGLPos(x, y);
    moveSelectionDelta(
        -worldPos[0] + worldPosOrigin[0],
        -worldPos[1] + worldPosOrigin[1]);
  }

  /**
   * A key stroke was received by the OpenGL canvas but it wasn't used by the
   * previous layers.  Give someone else a chance to use it.
   *
   * This is intended to be overridden, and {@code super.uncaughtKey()}
   * should be the final statement of such methods.  Normally, this is the
   * "last gasp" key handler.
   */
  public void uncaughtKey(KeyEvent event,
      boolean keyCtrlState, boolean keyAltState, boolean keyShiftState) {

    switch (event.keyCode) {
      case SWT.ARROW_UP:
        if ((event.stateMask & SWT.CTRL) != 0) {
          rotateCamera(-0.5f, 0.0f, 0.0f);
        } else {
          pedestalCamera(-5.0f);
        }
        break;
      case SWT.ARROW_DOWN:
        if ((event.stateMask & SWT.CTRL) != 0) {
          rotateCamera(0.5f, 0.0f, 0.0f);
        } else {
          pedestalCamera(5.0f);
        }
        break;

      case SWT.ARROW_LEFT:
        if ((event.stateMask & SWT.CTRL) != 0) {
          rotateCamera(0.0f, 0.0f, -0.5f);
        } else {
          truckCamera(-5.0f);
        }
        break;
      case SWT.ARROW_RIGHT:
        if ((event.stateMask & SWT.CTRL) != 0) {
          rotateCamera(0.0f, 0.0f, 0.5f);
        } else {
          truckCamera(5.0f);
        }
        break;

      case SWT.PAGE_UP:
        zoomCamera(5.0f);
        break;
      case SWT.PAGE_DOWN:
        zoomCamera(-5.0f);
        break;
      case SWT.HOME:
        homeCamera();
        break;

      default:
        logUncaughtKey(event.keyCode, event.character, keyCtrlState, keyAltState, keyShiftState);
    }

  }

  private void logUncaughtKey(int keyCode, char character,
      boolean keyCtrlState, boolean keyAltState, boolean keyShiftState) {
    StringBuffer buf = new StringBuffer();
    buf.append("Lost key press: ");
    buf.append(keyCode);

    buf.append(" (");
    buf.append(character);

    if (keyCtrlState) {
      buf.append(" CTRL");
    }
    if (keyAltState) {
      buf.append(" ALT");
    }
    if (keyShiftState) {
      buf.append(" SHFT");
    }

    buf.append(")");
    logger.info(buf.toString());
  }

  /**
   * Retrieve the openGL ids that were hited by a previous picking operation.
   *
   * @param hits
   * @param buffer
   * @return
   */
  private int[] processHits(int hits, IntBuffer buffer) {
    if (hits == 0) {
      return EMPTY_HIT_LIST;
    }
    if (hits < 0) {
      logger.warning("Too many hits!!" +
          " IntBuffer capacity = " + buffer.capacity());
      return EMPTY_HIT_LIST;
    }
    int[] hitsResults = new int[hits];
    int offset = 0;
    int names;
    for (int i = 0; i < hits; i++) {
      names = buffer.get(offset); offset++;
      offset++; // z1 (first z)
      offset++; // z2 (last z)

      for (int j = 0; j < names; j++) {
        if (j == (names - 1)) {
          hitsResults[i] = buffer.get(offset);
        }
        offset++;
      }
    }
    logger.fine("hits = " + hits + "; offset = " + offset);
    return hitsResults;
  }

  /**
   * Pick the object at the given mouse position (in window-relative
   * coordinates)
   *
   * @param mouseX
   * @param mouseY
   * @return ids of picked objects.
   */
  public int[] pickObjectsAt(int mouseX, int mouseY) {
    this.mouseX = mouseX;
    this.mouseY = mouseY;
    this.selectionWidth = 1;
    this.selectionHeight = 1;
    return renderWithPicking();
  }

  /**
   * The same as pickObjectAt, but with a rectangle.
   *
   * @param startX
   * @param startY
   * @param toX
   * @param toY
   * @return ids of picked objects.
   */
  public int[] pickRectangle(int startX, int startY, int toX, int toY) {
    if (startX < toX) {
      this.selectionWidth = toX - startX;
      this.mouseX = startX + selectionWidth / 2;
    } else {
      this.selectionWidth  = startX - toX;
      this.mouseX = toX + selectionWidth / 2;
    }
    if (startY < toY) {
      this.selectionHeight = toY - startY;
      this.mouseY = startY + selectionHeight / 2;
    } else {
      this.selectionHeight  = startY - toY;
      this.mouseY = toY + selectionHeight / 2;
    }
    drawSelectRectangle = false;
    return renderWithPicking();
  }

  /**
   * Activate and define an overlay rectangle that shows the selection area.
   *
   * @param fromX
   * @param fromY
   * @param mouseX
   * @param mouseY
   */
  public void activateSelectionRectangle(
      int fromX, int fromY, int mouseX, int mouseY) {
    drawSelectRectangle = true;
    this.mouseX = mouseX;
    this.mouseY = mouseY;
    this.startSelectX = fromX;
    this.startSelectY = fromY;
  }

  /**
   * Start 2D rendering mode.
   */
  protected void go2D() {
    /* Disable depth testing */
    gl.glDisable(GL2.GL_DEPTH_TEST);
    /* Select The Projection Matrix */
    gl.glMatrixMode(GL2.GL_PROJECTION);
    /* Store The Projection Matrix */
    gl.glPushMatrix();
    /* Reset The Projection Matrix */
    gl.glLoadIdentity();
    /* Set Up An Ortho Screen */
    glu.gluOrtho2D(0.0, 1.0d, 0, 1.0d);
    /* Select The Modelview Matrix */
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    /* Stor the Modelview Matrix */
    gl.glPushMatrix();
    /* Reset The Modelview Matrix */
    gl.glLoadIdentity();
  }

  /**
   * Return to 3D rendering mode.
   */
  protected void end2D() {
    /* Select The Projection Matrix */
    gl.glMatrixMode(GL2.GL_PROJECTION);
    /* Restore The Old Projection Matrix */
    gl.glPopMatrix();
    /* Select the Modelview Matrix */
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    /* Restore the Old Projection Matrix */
    gl.glPopMatrix();
    /* Re-enable Depth Testing */
    gl.glEnable(GL2.GL_DEPTH_TEST);
  }

  /**
   * Draw the 2D selection rectangle on top of everything.
   */
  protected void drawRectangle() {
    int[] viewPort = new int[4];
    gl.glGetIntegerv(GL2.GL_VIEWPORT, viewPort, 0);

    // start point
    float sx = (float) startSelectX / (float) viewPort[2];
    float sy = (float) (viewPort[3] - startSelectY) / (float) viewPort[3];
    // end point
    float ex = (float) mouseX / (float) viewPort[2];
    float ey = (float) (viewPort[3] - mouseY) / (float) viewPort[3];

    go2D();

    gl.glColor4f(0.0f, 0.0f, 0.8f, 0.3f);
    gl.glBegin(GL2.GL_QUADS);
    gl.glVertex2f(sx, sy);
    gl.glVertex2f(ex, sy);
    gl.glVertex2f(ex, ey);
    gl.glVertex2f(sx, ey);
    gl.glEnd();

    gl.glColor4f(0.0f, 0.0f, 0.6f, 1.0f);
    gl.glLineWidth(1.0f);
    gl.glBegin(GL2.GL_LINE_STRIP);
    gl.glVertex2f(sx, sy);
    gl.glVertex2f(ex, sy);
    gl.glVertex2f(ex, ey);
    gl.glVertex2f(sx, ey);
    gl.glVertex2f(sx, sy);
    gl.glEnd();

    end2D();
  }

  /**
   * Setup scene colors.
   *
   * @param back
   * @param front
   */
  public void setColors(Color back, Color front) {
    gl.glClearColor(back.getRed() / 255f,
        back.getGreen() / 255f,
        back.getBlue() / 255f,
        back.getAlpha() / 255f);
    this.foregroundColor = front;
  }

  public Color getForegroundColor() {
    return foregroundColor;
  }

  public GLCanvas getContext() {
    return canvas;
  }

  public BufferedImage takeScreenshot() {
    if (!this.canvas.isCurrent()) {
      this.canvas.setCurrent();
    }
    context.makeCurrent();
    Point size = canvas.getSize();
    BufferedImage image = Screenshots.grab(size.x, size.y);
    context.release();
    return image;
  }

  ////////////////////////
  // Static functions for hyperbolic view coordinates calculations.


  public static float[] P(float x, float y) {
    if (!hyperbolic) {
      return new float[]{x, y, 0.0f};
    } else {
      // FIXME: uncomment this line, when fixed. we must use xoff here...
//      return convertVertex(x+grip.xoff - FACTOR/2.0f, y+grip.yoff-FACTOR/2.0f);
      return convertVertex(x - FACTOR/2.0f, y-FACTOR/2.0f);
    }
  }

  public static void V(GL2 gl, float x, float y) {
    if (!hyperbolic) {
      gl.glVertex2f(x, y);
    } else {
      //FIXME: uncomment. when grip static is totally fixed
      //x = x+grip.xoff;
      //y = y+grip.yoff;
      convertGLVertex(gl, x-FACTOR/2.0f, y-FACTOR/2.0f);
    }
  }

  public static void V(GL2 gl, double x, double y) {
    V(gl, (float)x, (float)y);
  }

  public static float[] convertVertex(float x, float y) {
    return convertVertex(x,y,0,1,1,1,0,0,0);
  }

  public static float[] convertVertex(float x, float y, float z) {
    return convertVertex(x,y,z,1,1,1,0,0,0);
  }

  public static float[] convertVertex(float x, float y, float z, float a, float b, float c, float k, float h, float p) {
    // solve the equation:
    // - (x-k)^2 / a^2 - (y-h)^2 / b^2 + (z-l)^2 / c^2 = 1
    // to get z.
    float zoom = 1.0f;
    x /= zoom;
    y /= zoom;
    z +=
      -Math.sqrt(
          c*c* (
            (x-k) * (x-k) / (a*a)
            + (y-h) * (y-h) / (b*b)
            + 1))
      + p;

    return new float[]{x, y, z};
  }

  public static void convertGLVertex(GL2 gl, float x, float y) {
    convertGLVertex(gl, x,y,0,1,1,1,0,0,0);
  }

  public static void convertGLVertex(GL2 gl, float x, float y, float z) {
    convertGLVertex(gl, x,y,z,1,1,1,0,0,0);
  }

  public static void convertGLVertex(GL2 gl, float x, float y, float z, float a, float b, float c, float k, float h, float p) {
    // solve the equation:
    // - (x-k)^2 / a^2 - (y-h)^2 / b^2 + (z-l)^2 / c^2 = 1
    // to get z.
    float zoom = 1.0f;
    x /= zoom;
    y /= zoom;
    z +=
      -Math.sqrt(
          c*c* (
            (x-k) * (x-k) / (a*a)
            + (y-h) * (y-h) / (b*b)
            + 1))
      + p;

    //System.out.println(""+x+":"+y+":"+z);
    gl.glVertex3f(x,y,z);
  }

}
TOP

Related Classes of com.google.devtools.depan.eclipse.visualization.ogl.GLScene

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.