Package de.fhpotsdam.unfolding.interactions

Source Code of de.fhpotsdam.unfolding.interactions.TuioCursorHandler

package de.fhpotsdam.unfolding.interactions;

import java.util.Arrays;
import java.util.List;
import java.util.Stack;

import org.apache.log4j.Logger;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PVector;
import TUIO.TuioClient;
import TUIO.TuioCursor;
import TUIO.TuioListener;
import TUIO.TuioObject;
import TUIO.TuioPoint;
import TUIO.TuioTime;
import de.fhpotsdam.unfolding.UnfoldingMap;
import de.fhpotsdam.unfolding.events.MapEventBroadcaster;
import de.fhpotsdam.unfolding.events.PanMapEvent;
import de.fhpotsdam.unfolding.events.ZoomMapEvent;
import de.fhpotsdam.unfolding.geo.Location;

/**
* Handles finger input (TUIO cursors) from the user and broadcasts MapEvents such as zoom and pan.
*/
// FIXME Implement tuio to map interactions as events!
// REVISIT Check how to use simpletouch's TuioTransformableObject. For use in events, as in here.
public class TuioCursorHandler extends MapEventBroadcaster implements TuioListener {

  public static Logger log = Logger.getLogger(TuioCursorHandler.class);

  private PApplet p;

  protected TuioClient tuioClient;

  TuioCursor tuioCursor1;
  TuioCursor tuioCursor2;

  Stack<TuioCursor> unusedTuioCursorStack = new Stack<TuioCursor>();

  float oldAngle;
  float oldDist;
  PFont font;

  public TuioCursorHandler(PApplet p, UnfoldingMap... maps) {
    this(p, Arrays.asList(maps));
  }

  public TuioCursorHandler(PApplet p, boolean listenToTuio, UnfoldingMap... maps) {
    this(p, listenToTuio, Arrays.asList(maps));
  }

  public TuioCursorHandler(PApplet p, boolean listenToTuio, List<UnfoldingMap> maps) {
    super(maps);

    this.p = p;
    this.font = p.createFont("Sans-Serif", 12);

    tuioClient = new TuioClient();
    if (listenToTuio) {
      tuioClient.addTuioListener(this);
    }
    tuioClient.connect();

    p.registerDispose(this);
  }

  public TuioCursorHandler(PApplet p, List<UnfoldingMap> maps) {
    this(p, true, maps);
  }

  public void dispose() {
    tuioClient.disconnect();
  }

  public TuioClient getTuioClient() {
    return tuioClient;
  }

  public void updateTuioCursor(TuioCursor tcur) {
    int x = tcur.getScreenX(p.width);
    int y = tcur.getScreenY(p.height);

    // Updates go to all hit ones, independent of z-index
    for (UnfoldingMap map : maps) {
      if (map.isHit(x, y)) {

        if (tuioCursor1 != null && tuioCursor2 != null) {
          // Two fingers: Zoom + Rotate
          // FIXME In very high zoom levels zooming is off. Precision problem?
          // (float/double)

          // Uses inner zoom and inner rotation, thus fingers are on same location

          // Flags to test various combinations
          boolean zoom = true;
          boolean rotate = true;

          PVector transCenter = new PVector();
          if (tuioCursor2.getCursorID() == tcur.getCursorID()) {
            transCenter.x = tuioCursor1.getScreenX(p.width);
            transCenter.y = tuioCursor1.getScreenY(p.height);
          } else {
            transCenter.x = tuioCursor2.getScreenX(p.width);
            transCenter.y = tuioCursor2.getScreenY(p.height);
          }

          if (zoom) {
            ZoomMapEvent zoomMapEvent = new ZoomMapEvent(this, map.getId(), ZoomMapEvent.ZOOM_BY);
           
            // 3 variations to zoom with two finger gestures
            // TODO Do study on usability for different interaction approaches.
           
            // 1. pos of last finger
            Location centerLocation = map.getLocation(transCenter.x, transCenter.y);
            zoomMapEvent.setTransformationCenterLocation(centerLocation);

            // 2. object center: pinch gesture w/o fixed finger-location connection
            // float[] objectCenterXY =
            // map.mapDisplay.getScreenFromObjectPosition(map.mapDisplay.getWidth()/2,
            // map.mapDisplay.getHeight()/2);
            // PVector objectCenter = new PVector(objectCenterXY[0], objectCenterXY[1]);
            // map.mapDisplay.setInnerTransformationCenter(objectCenter);

            // 3. middle pos between both fingers

            float newDist = getDistance(tuioCursor1, tuioCursor2);
            float scaleDelta = newDist / oldDist;
            oldDist = newDist;
            zoomMapEvent.setZoomDelta(scaleDelta);

            eventDispatcher.fireMapEvent(zoomMapEvent);
          }

          if (rotate) {
           
            // TODO Use events (instead of direct map manipulation)

            // rotate center
            map.mapDisplay.setTransformationCenter(transCenter);

            float newAngle = getAngleBetween(tuioCursor1, tuioCursor2);
            float angle = newAngle - oldAngle;
            oldAngle = newAngle;
            map.rotate(angle);
          }


        } else if (tuioCursor1 != null) {
          // One finger: pan

          if (tuioCursor1.getCursorID() == tcur.getCursorID()) {
            TuioPoint oldTuioPoint = tcur.getPath().get(tcur.getPath().size() - 2);
            Location fromLocation = map.mapDisplay.getLocation(oldTuioPoint.getScreenX(p.width),
                oldTuioPoint.getScreenY(p.height));
            Location toLocation = map.mapDisplay.getLocation(tuioCursor1.getScreenX(p.width),
                tuioCursor1.getScreenY(p.height));

            PanMapEvent panMapEvent = new PanMapEvent(this, map.getId(), PanMapEvent.PAN_BY);
            panMapEvent.setFromLocation(fromLocation);
            panMapEvent.setToLocation(toLocation);
            eventDispatcher.fireMapEvent(panMapEvent);
          }
        }
      }
    }
  }

  public void addTuioCursor(TuioCursor tuioCursor) {
    if (tuioCursor1 == null) {
      tuioCursor1 = tuioCursor;
    } else if (tuioCursor2 == null) {
      tuioCursor2 = tuioCursor;

      oldAngle = getAngleBetween(tuioCursor1, tuioCursor2);
      oldDist = getDistance(tuioCursor1, tuioCursor2);
    } else {
      // PApplet.println("Already 2 cursors in use. Adding further ones to stack.");
      unusedTuioCursorStack.add(tuioCursor);
    }
  }

  public void removeTuioCursor(TuioCursor tuioCursor) {
    if (tuioCursor2 != null && tuioCursor2.getCursorID() == tuioCursor.getCursorID()) {
      tuioCursor2 = null;

      if (!unusedTuioCursorStack.isEmpty()) {
        tuioCursor2 = unusedTuioCursorStack.pop();
        oldAngle = getAngleBetween(tuioCursor1, tuioCursor2);
        oldDist = getDistance(tuioCursor1, tuioCursor2);
      }

    }

    if (tuioCursor1 != null && tuioCursor1.getCursorID() == tuioCursor.getCursorID()) {
      tuioCursor1 = null;
      // If second still is on object, switch cursors
      if (tuioCursor2 != null) {
        tuioCursor1 = tuioCursor2;

        if (!unusedTuioCursorStack.isEmpty()) {
          tuioCursor2 = unusedTuioCursorStack.pop();
          oldAngle = getAngleBetween(tuioCursor1, tuioCursor2);
          oldDist = getDistance(tuioCursor1, tuioCursor2);
        } else {
          tuioCursor2 = null;
        }
      }
    }

    // TODO check only if the other were no hits
    TuioCursor toRemoveTC = null;
    for (TuioCursor unusedTuioCursor : unusedTuioCursorStack) {
      if (tuioCursor.getCursorID() == unusedTuioCursor.getCursorID()) {
        toRemoveTC = tuioCursor;
        break;
      }
    }
    if (toRemoveTC != null) {
      unusedTuioCursorStack.remove(toRemoveTC);
    }

  }

  protected float getDistance(TuioCursor tuioCursor1, TuioCursor tuioCursor2) {
    return PApplet.dist(tuioCursor1.getScreenX(p.width), tuioCursor1.getScreenY(p.height),
        tuioCursor2.getScreenX(p.width), tuioCursor2.getScreenY(p.height));
  }

  protected float getAngleBetween(TuioCursor tuioCursor1, TuioCursor tuioCursor2) {
    return getAngleBetween(tuioCursor1.getScreenX(p.width), tuioCursor1.getScreenY(p.height),
        tuioCursor2.getScreenX(p.width), tuioCursor2.getScreenY(p.height));
  }

  protected float getAngleBetween(float x1, float y1, float x2, float y2) {
    float difY = y1 - y2;
    float difX = x1 - x2;
    float angle = PApplet.atan2(difY, difX);
    return angle;
  }

  @Override
  public void addTuioObject(TuioObject arg0) {
  }

  @Override
  public void removeTuioObject(TuioObject arg0) {
  }

  @Override
  public void updateTuioObject(TuioObject arg0) {
  }

  @Override
  public void refresh(TuioTime arg0) {
  }

  /**
   * Draws all TuioCursors.
   */
  public void drawCursors() {
    for (TuioCursor tuioCursor : tuioClient.getTuioCursors()) {
      drawCursor(tuioCursor);
    }
  }

  /**
   * Draws a TuioCursor as small circle with ID as label.
   *
   * @param tc
   *            The cursor to draw.
   */
  public void drawCursor(TuioCursor tc) {
    if (tc == null)
      return;

    p.textFont(font);
    // p.stroke(0, 20);
    p.noStroke();
    p.fill(80, 30);
    p.ellipse(tc.getScreenX(p.width), tc.getScreenY(p.height), 27, 27);
    p.fill(80, 80);
    p.ellipse(tc.getScreenX(p.width), tc.getScreenY(p.height), 25, 25);
    p.fill(0, 200);
    p.textSize(12);
    p.text(tc.getCursorID(), tc.getScreenX(p.width) - 4, tc.getScreenY(p.height) + 4);
  }

}
TOP

Related Classes of de.fhpotsdam.unfolding.interactions.TuioCursorHandler

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.