Package tripleplay.gesture

Source Code of tripleplay.gesture.Swipe

//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2014, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.gesture;

import java.util.HashMap;
import java.util.Map;

import pythagoras.f.Point;

/**
* A simple swipe gesture in a given cardinal direction. Supports 1 to 4 fingers. If greedy, will
* indicate a continuous swipe. Completes if: one of the fingers stops moving or is removed from the
* display. Cancels if an additional finger is added to the display or one of the fingers switches
* directions.
*/
public class Swipe extends GestureBase<Swipe>
{
    public Swipe (Direction direction) {
        this(1, direction);
    }

    public Swipe (int touches, Direction direction) {
        if (touches < 1 || touches > 4) {
            Log.log.warning("How many fingers do you think people have?", "tapTouches", 4);
            touches = Math.max(1, Math.min(4, touches));
        }
        if (direction == null) {
            Log.log.warning("Swipe cannot operate with a null direction, assuming RIGHT");
            direction = Direction.RIGHT;
        }
        _touches = touches;
        setDirection(direction);
    }

    /**
     * If true (the default) a pause counts the same as a finger lifted from the display.
     */
    public Swipe cancelOnPause (boolean value) {
        _cancelOnPause = value;
        return this;
    }

    /**
     * A swipe is not qualified if the user moves outside of a region defined by lines that are
     * parallel to the line that goes through the start touch point along the direction axis.
     * offAxisTolerance is the distance away from that defining line that the parallel boundaries
     * sit. The default is 10 pixels. If you want the user to have more freedom in how finely
     * defined their swipes are, make the tolerance large.
     */
    public Swipe offAxisTolerance (int pixels) {
        _offAxisTolerance = pixels;
        return this;
    }

    /**
     * The distance on the Swipe's Direction axis that a touch must move to qualify the Swipe for
     * completion.
     */
    public Swipe onAxisThreshold (int pixels) {
        _onAxisThreshold = pixels;
        return this;
    }

    /**
     * An axis Swipe will consider any movement along the axis of its configured direction to be
     * valid, rather than only movement in the direction itself.
     */
    public Swipe axisSwipe (boolean value) {
        _axisSwipe = value;
        return this;
    }

    @Override protected void clearMemory () {
        _movedEnough = false;
        _startNodes.clear();
        _lastNodes.clear();
    }

    @Override protected void updateState (GestureNode node) {
        switch (node.type) {
        case START:
            _startNodes.put(node.touch.id(), node);
            break;

        case MOVE:
            // always grounds for immediate dismissal
            if (_startNodes.size() != _touches) setState(State.UNQUALIFIED);
            evaluateMove(node);
            break;

        case PAUSE:
            if (_cancelOnPause) setState(getEndState());
            break;

        case END:
            setState(getEndState());
            break;

        case CANCEL:
            setState(State.UNQUALIFIED);
            break;
        }
    }

    protected State getEndState () {
        return _movedEnough && _startNodes.size() == _touches ? State.COMPLETE : State.UNQUALIFIED;
    }

    // TODO: any gesture that cares about swiping in a cardinal direction could make use of this
    protected void evaluateMove (GestureNode node) {
        GestureNode start = _startNodes.get(node.touch.id());
        if (start == null) {
            Log.log.warning("No start point for a path check, invalid state",
                "touchId", node.touch.id());
            return;
        }

        GestureNode lastNode = _lastNodes.get(node.touch.id());
        Point current = node.location();
        _lastNodes.put(node.touch.id(), node);
        // we haven't moved far enough yet, no further evaluation needed.
        Point startLoc = start.location();
        if (current.distance(startLoc) < _onAxisThreshold) return;

        float offAxisDistance; // distance from our start position in the perpendicular axis
        float lastAxisDistance = axisDistance(
            lastNode == null ? null : lastNode.location(), current);
        if (_direction == Direction.UP || _direction == Direction.DOWN)
            offAxisDistance = Math.abs(current.x() - startLoc.x());
        else
            offAxisDistance = Math.abs(current.y() - startLoc.y());

        // if we've strayed outside of the safe zone, or we've backtracked from our last position,
        // disqualify
        if (offAxisDistance > _offAxisTolerance) setState(State.UNQUALIFIED);
        else if (lastAxisDistance < 0) backtracked(node, -lastAxisDistance);

        // Figure out if we've moved enough to meet minimum requirements with all touches
        if (!_movedEnough) {
            boolean allMovedEnough = true;
            for (Map.Entry<Integer, GestureNode> touchStart : _startNodes.entrySet()) {
                GestureNode touchLast = _lastNodes.get(touchStart.getKey());
                if (axisDistance(touchStart.getValue().location(),
                    touchLast == null ? null : touchLast.location()) <=
                    _onAxisThreshold) {
                    allMovedEnough = false;
                    break;
                }
            }
            if (allMovedEnough) {
                _movedEnough = true;
                if (_greedy) setState(State.GREEDY);
            }
        }
    }

    protected float axisDistance (Point start, Point end) {
        if (start == null || end == null) return 0;

        float value;
        if (_direction == Direction.UP || _direction == Direction.DOWN)
            value = end.y() - start.y();
        else
            value = end.x() - start.x();
        return _axisSwipe ? Math.abs(value) : value * _directionModifier;
    }

    protected void backtracked (GestureNode node, float distance) {
        if (!_axisSwipe) {
            setState(State.UNQUALIFIED);
        }
    }

    protected void setDirection (Direction direction) {
        _direction = direction;
        _directionModifier = _direction == Direction.UP || _direction == Direction.LEFT ? -1 : 1;
    }

    protected int _touches;
    protected Direction _direction;
    protected int _directionModifier;

    protected boolean _movedEnough = false;
    protected Map<Integer, GestureNode> _startNodes = new HashMap<Integer, GestureNode>();
    protected Map<Integer, GestureNode> _lastNodes = new HashMap<Integer, GestureNode>();
    protected boolean _cancelOnPause = true;
    protected int _offAxisTolerance = 10;
    protected int _onAxisThreshold = 10;
    protected boolean _axisSwipe = false;
}
TOP

Related Classes of tripleplay.gesture.Swipe

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.