# Moving circles along a ray to eliminate location check

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(),
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 ),

$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 the check at all. Those two lines are basically saying, "If the circle is on the left/top do it one way, and if it's on the right/bottom do it another way". I feel like I'm missing something earlier in the function that could be solved by switching the sides of operators, adding instead of subtracting, or getting the absolute value. • a = Math.abs( is why you have to check. Are you sure you need absolute values? – James Khoury Dec 14 '11 at 0:56 ## 1 Answer 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 );


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.

• WOW. I'm not worthy. 20 lines to 3. Up-vote this, people. My demo page gets a lot of hits. I put a link to your SO profile in there with credit. – ThinkingStiff Dec 14 '11 at 4:12
• @ThinkingStiff thanks. Don't worry we all do this. It took me a couple of goes till i really sat back and thought about it. It happens that I was recently working on some angle/circle/distance stuff myself. – James Khoury Dec 14 '11 at 4:37
• @ThinkingStiff I just added some other suggestions for the rest of the code which don't really change a great deal. – James Khoury Dec 14 '11 at 5:07
• Both good changes, updated. Thanks. Who knows how I ended up at a ternary boolean. :) The circle in setLocations() was a last minute change I made to get ready for this post. It was called hovered or something nonintuitive before. Good catch. – ThinkingStiff Dec 14 '11 at 21:13