views:

418

answers:

4

I need to constrain a point inside a DisplayObject given to me by the artist.

I got it working but only for the occations where the cursor is still inside bounds.
The limited object is called limited.

function onSqMouseMove(event:MouseEvent) {
    if(bounds.hitTestPoint(event.stageX, event.stageY, true)) {
        limited.x = event.stageX;
        limited.y = event.stageY;
    } else {
        /* Find closest point in the Sprite */
    }
}

limited.addEventListener(MouseEvent.MOUSE_DOWN, function(event:MouseEvent) {
    stage.addEventListener(MouseEvent.MOUSE_MOVE, onSqMouseMove);
});

limited.addEventListener(MouseEvent.MOUSE_UP, function(event:MouseEvent) {
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, onSqMouseMove);
});

How do I go about implementing the other half of the function? I am aware Sprite's startDrag accepts arguments, where the second one is the constraint rectangle, but in my case, bounds are an arbitrary shape.

When the object is dragged outside the bounds, I want to calculate the closest point from the cursor to bounds' polygon.

Just to note that bounds can have 'holes'.

Edit:

To be clear, I don't want to find if a point is inside the MovieClip or not, I want the closest point from a point outside the MovieClip (note that hitTestPoint fails!) to the bounds of it.

alt text

+1  A: 

I saw this guys blog once a while back on something about "pixel precision on images", where he was able to something really cool with images and hit tests, but I can't remember :p.

How about these for a start:

Given that your DisplayObject is a solid color, but an arbitrary shape, it would be really easy going by Doug McCune's tutorial. Let me know if that gets you closer.

Best, Lance

viatropos
First two are useless as I'm already using `hitTestPoint` which works great. I need to investigate more about the third link.
LiraNuna
If you need good pixel-perfect collision detection that works with nested DisplayObjects and/or rotation, etc. check out the Collision Detection Kit (CDK): http://code.google.com/p/collisiondetectionkit/
Cameron
Collision detection **IS NOT MY PROBLEM**. I want to get the closest point from outside the movie clip to it's bounds. Look at my edited post.
LiraNuna
+1  A: 

well, it really depends on your accuracy you are after as far as im concerned (and exactly what the style or complexity of the background image is). as far as i know there is no direct method of testing distance unless you have a specific mathmatical reference, therefore unless you are dynamically drawing your background, you are in a lot of trouble.

two methods come straight to mind:

  1. on loading of the level, compute it into an array of nth accuracy (5x5 pixel for example), copy a section of the background and then test if it contains transparency (getColorBoundsRect( 0xff000000, 0, false ); perhaps?). note which contain edges in the array, then you can test this grid against mouse position to tell where the most likely closest edge is (any one square apart, then two, then three etc). if you need greater accuracy then you can try some sort of computation once you know which ones are most likely to hold the closest pixel. This wont be pinpoint accurate and will require some computation at the start, but it should be quick to run.

  2. if you have a circular hitdetect sprite that you attach to the cursor position, then you can expand/contract it and find the hitpoint. starting at width:0, height:0 for when it is in contact with the background, when it moves over the edge expand it by a reasonable amount(eg 5px) per loop until it has a hittest. you can then track this point (as demonstrated in the Grant Skinners Page as viatropos posted), if the collision area is too large then your cursor is moving closer to the collision point, and can reduce by an amount until the collision size is sensible again. this is a bit intensive on processor, but its only a few hittests a frame, same as having 30 balls bouncing around, it shouldnt be too hard in it.

hope this gives you some ideas!

shortstick
+1  A: 

You can start at the mouse position and start spiraling out doing the hit test for every pixel. This will result in a long loop but depending on your use case it should probably perform reasonably ok. This you can only see by testing.

Bart van Heukelom
A: 

There are multiple algorithmic methods that may work well, depending upon your situation:

  1. Preprocess the artwork and mark points every 20 pixels or so. This is to reduce the number of distance tests you need to perform. If the drag object is outside the bounds, then determine the closest reference point. Determine which of its two neighboring reference points is closest to the drag object. Pick some point between them based upon the ratio of distance (if they are nearly equidistant, it would imply the closest point is almost halfway). Something like this: Point.interpolate(closestRef, nextClosestRef, factor). The drawbacks of this method would be that it would not be 100% accurate, since areas in between the reference points would be reduced to straight lines (which may influence the placement of reference points). In other words, if your bounding art has lots of complex edges, this method is not good, if on the other hand it has lots of straight lines, it's great.
  2. On an abstract level, your problem is called the "nearest-neighbor problem". A google search may turn up some efficient solution.
  3. Similar to what shortstick described above, attach a circular hitdetect sprite to the cursor position. However, rather than incrementing the radius by 5px each time, double it each time until it intersects the bounding shape. Then perform a binary search using the last two radius lengths used as the lower and upper bounds. This will give you the distance to the closest point on the bounding shape. To translate that to an actual point is a bit more difficult, but not impossible. You just need a secondary partial circle (not sure exactly how this is called) hitdetect sprite attached. Adjust the arc length in a similar binary search to determine the angle. Now do Point.polar(radius, angle).add(cursorPosition) to get the actual point on the border of the shape.
  4. I'm pretty sure that there's a way to do 3, but using rectangles, which have a native implementation in the Flash Platform. I'm not entirely sure though, but it's worth some thought.

Just for reference, it's always a good idea to dig through the API first and see if there's already a library that does what you need.

SEK
"it's always a good idea to dig through the API first" -- Do you think I would offer a bounty for 200rep with no answers if it was *that* simple?! Of course I read the API reference!
LiraNuna