Reputation: 67292
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'.
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.
(source: liranuna.com)
Upvotes: 3
Views: 1667
Reputation: 675
Hate to give another theoretical answer and open this up again, but I have been pondering this problem myself recently. I would assume that a ray-tracing method could work. The idea is as follows:
The only problem I can think of with this method, is that it might be susceptible to a local minima / maxima problem. It could also potentially be expensive to compute.
A quick google gave me the following resources, if you were to attempt the above (which I think I might be doing if I cannot find an alternative)
An example of what can be achieved if you had the point on the inside of the shape and found the nearest point on the surface (leading in most cases to the normal at that point), is this one on boids http://www.red3d.com/cwr/steer/Containment.html
Did you find a solution for this?
Upvotes: 0
Reputation: 1222
There are multiple algorithmic methods that may work well, depending upon your situation:
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.Point.polar(radius, angle).add(cursorPosition)
to get the actual point on the border of the shape.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.
Upvotes: 0
Reputation: 44124
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.
Upvotes: 1
Reputation: 2051
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:
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.
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!
Upvotes: 1
Reputation: 79458
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
Upvotes: 1