Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add constrained bounds to SimpleDragHandler #3

Closed
pixelzoom opened this issue Mar 7, 2013 · 10 comments
Closed

add constrained bounds to SimpleDragHandler #3

pixelzoom opened this issue Mar 7, 2013 · 10 comments

Comments

@pixelzoom
Copy link
Contributor

Try using SimpleDragHandler to drag a node that has constrained drag bounds. The cursor will become disconnected from the node.

Here's a use case (untested code). Note that node position is updated via a roundtrip to the model.


var mvt = new ModelViewTransform2D(…); // phetcommon
var location = new Property(…); // phetcommon
var node = new Text(…); // scenery

var constrainBounds = function ( point, bounds ) {
      if ( bounds === undefined || bounds.contains( point.x, point.y ) ) {
        return point;
      }
      else {
        var xConstrained = Math.max( Math.min( point.x, bounds.getMaxX() ), bounds.x );
        var yConstrained = Math.max( Math.min( point.y, bounds.getMaxY() ), bounds.y );
        return new Vector2( xConstrained, yConstrained );
      }
};

node.addInputListener( new SimpleDragHandler( {
     translate: function ( options ) {
          var pModel = mvt.viewToModel( new Vector2( options.position.x, options.position.y ) );
          var pModelConstrained = dragHandler.constrainBounds( pModel, movable.dragBounds );
          location.set( pModelConstrained );
        }
} );

location.addObserver( function( newLocation ) {
     var pView = mvt.modelToView( newLocation );
     node.x = pView.x;
     node.y = pView.y;
} );
@jonathanolson
Copy link
Contributor

A few options:

  1. Add { constrainedBounds: } support to SimpleDragHandler's parameter object. This would be in the global view bounds (since that is the coordinate frame of event.finger.point). Alternatively, { contrainedShape: }, with initial support for rectangles, but future support for finding the closest point on an arbitrary shape if it is outside. This has the benefit of the drag handler knowing about the constraint, and not needing to add { translate: ... } to override the positioning for general Scenery use.
  2. Handle the constraint like above, essentially in model code. It seems like no changes are necessary to Scenery if this is being done? Correct me if I'm wrong.
  3. something else.

@pixelzoom
Copy link
Contributor Author

I'm fine with (2), letting the client handle it. I think the client could correct the problem if it noted the drag start point (in "start" function), like we do in Piccolo. SR seemed to think that this should be handled in SimpleDragHandler, perhaps he can elaborate?

@samreid
Copy link
Member

samreid commented Mar 7, 2013

Can you take a look at phet/svn/RelativeDragHandler? It solves the problem of having an arbitrary constraint, never getting away from the cursor or hand, and playing nicely with MVC round trip.

//Drag the node to the constrained location
public void mouseDragged( PInputEvent event ) {
    //Make sure we started the drag already
    if ( relativeGrabPoint == null ) {
        updateGrabPoint( event );
    }

    //Compute the targeted model point for the drag
    final Point2D newDragPosition = event.getPositionRelativeTo( node.getParent() );
    Point2D modelPt = transform.viewToModel( newDragPosition.getX() - relativeGrabPoint.getX(),
                                             newDragPosition.getY() - relativeGrabPoint.getY() );

    //Find the constrained point for the targeted model point and apply it
    Point2D constrained = constraint.apply( modelPt );

    sendMessage( constrained );

    setModelPosition( constrained );
}

The constraint function could be something like ImmutableRectangle.getClosestPoint(x,y);

I think having something built in to scenery somewhere (maybe but not necessarily SimpleDragHandler) would be nice so we aren't duplicating this idea in lots of places in client code.

@jonathanolson
Copy link
Contributor

I personally would like to have the bounds handled by Scenery, and for the planned pan/zoom handler it absolutely needs to do similar bounds constraints.

Would passing in { constrain: <function( point ): truthy> } be a better option?

@samreid
Copy link
Member

samreid commented Mar 7, 2013

I guess you are suggesting {constrain: } as an option for SimpleDragHandler, and that the argument might be a function or a rectangle? Those options sound like they would work well. SimpleDragHandler may need to be rewritten a bit to use relativeGrabPoint or equivalent so the mouse/touch doesn't get away from the object.

@jonathanolson
Copy link
Contributor

I would imagine 'constrain' could be a Bounds2, a function of that type above, or in the future a Shape once it can support Shape.closestPointTo( point ). Bounds2 does not have an invertible transform, since when it rotates it expands to stay axis-aligned, so if we need rotated rectangle constraints we'll have to use the Shape form (i.e. Rectangle)

@pixelzoom
Copy link
Contributor Author

Thinking about this more... I don't think drag constraints should be added to SimpleDragHandler. It's supposed to be "simple". I think it would be more appropriate to write BoundedDragHandler (or whatever you want to call it.)

@ghost ghost assigned jonathanolson Mar 11, 2013
@jonathanolson
Copy link
Contributor

As noted during a developer meeting, I'll be trying out a mixin-style for drag handlers, so we could potentially mix in a BoundedDragBehavior into a simpler drag handler design, as it should allow more extension possibilities.

@pixelzoom
Copy link
Contributor Author

This functionality should be added to DragListener, not SimpleDragHandler. See #131.

@jonathanolson
Copy link
Contributor

Will track in #131.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants