HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Moving circles along a ray to eliminate location check

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
raycircleseliminatemovingalongchecklocation

Problem

I have a function that moves circles out of the way of an expanding circle. It moves them along a ray from the center of the expanding circle through the center of the circle to move. This allows all surrounding circles to move without overlapping.

Here's a diagram to better explain this:

I expand the size of the red circle from 100px to 200px. I want to move all the blue circles out of the way by half the difference ( (200-100)/2 = 50px, in this example ) along the gray line (with the gray line being different for each blue circle).

jsFiddle

$this.siblings( ".circle" ).each( function() {

    var $this = $( this ),
        circle = $this.data(),
        circleX = circle.left + circle.radius,
        circleY = circle.top + circle.radius,
        a = Math.abs( hoveredY - circleY ),
        b = Math.abs( hoveredX - circleX ),
        c = Math.sqrt( ( a*a ) + ( b*b ) ),
        A = Math.acos( b / c ), 
        C = 90 * ( Math.PI / 180 ),
        B = C - A,
        sinA = Math.sin( A ),
        sinB = Math.sin( B ),
        sinC = Math.sin( C ),
        newc = c + ( expand / 2 ),
        newa = ( newc * sinA ) / sinC,
        newb = ( newc * sinB ) / sinC,
        newX = hoveredX + ( hoveredX > circleX ? -newb : newb ),
        newY = hoveredY + ( hoveredY > circleY ? -newa : newa ),
        left = newX - circle.radius,
        top = newY - circle.radius;

    $this.animate( { 

        "left": left,
        "top": top

    }, 75 );

});


hoveredX and hoveredY are the left and top coordinates of the red circle and expand is how much the red circle is expanding in pixels.

Mathematically, I'm sure there is a better way to do this, but I got this way working, and it's fine (although I'm not opposed hearing better solutions). But the one part I don't like about this function are the two lines:

newX = hoveredX + ( hoveredX > circleX ? -newb : newb ),
newY = hoveredY + ( hoveredY > circleY ? -newa : newa ),


What bugs me is that I'm having to do th

Solution

I had two thoughts. One was ... You really only need an angle and a distance. The distance is the amount you expand and the angle can be derived from the x/y coordinates.

http://jsfiddle.net/uLu7v/38/

first you calculate the angle:

var angle = Math.atan2(hoveredY - circleY, hoveredX - circleX);


Then you calculate the distance they move:

var topMove = ((expand /2 ) * Math.sin(angle)); // sin for Y
var leftMove = ((expand /2 ) * Math.cos(angle)); // cos for X


then use jQuery's built in animate +=:

$this.animate( { 
    "left": "-=" + leftMove + "px",
    "top":  "-=" + topMove + "px"
}, 75 );


Tada!

Edit:- A couple of improvements in the code not supplied in the question:

In function inCircle() change the return statement to return (mouseDistance <= radius); Using the ternary operator to return a boolean? ... I'm sure it used to return some other values right?

In $( ".circle" ).click( :

if(!$( this ).data( "clicked" ) && inCircle( $( this ), event.pageX, event.pageY ) ) {

    $( this ).data( "clicked", true );
    setLocations( this, 200, event );

} else {

    resetLocations();
    $( this ).data( "clicked", false );

};


Makes it cleaner but not a real improvement otherwise, more of a preference.

In function setLocations() your circle parameter conflicts with the local circle variable. I would change the parameter to circleElement.

Code Snippets

var angle = Math.atan2(hoveredY - circleY, hoveredX - circleX);
var topMove = ((expand /2 ) * Math.sin(angle)); // sin for Y
var leftMove = ((expand /2 ) * Math.cos(angle)); // cos for X
$this.animate( { 
    "left": "-=" + leftMove + "px",
    "top":  "-=" + topMove + "px"
}, 75 );
if(!$( this ).data( "clicked" ) && inCircle( $( this ), event.pageX, event.pageY ) ) {

    $( this ).data( "clicked", true );
    setLocations( this, 200, event );

} else {

    resetLocations();
    $( this ).data( "clicked", false );

};

Context

StackExchange Code Review Q#6807, answer score: 3

Revisions (0)

No revisions yet.