0

What I am aiming to do is arc the position of a circle towards the position of the mouse cursor, this all being relative to the world viewed through the canvas. To keep a handle on the speed at which the circle moves I decided to make a boundary larger than the circle, if the mouse is outside the boundary then the "position" of the mouse is brought to the boundary so that when I arc towards the coords, if they arent super far from the position of the circle it doesnt move at crazy speeds. I have this working and this is the code which does it:

    dx = Game.controls.mouseX - (this.x - xView); // get the distance between the x coords
    dy = Game.controls.mouseY - (this.y - yView); // get the distance between the y coords
    radii = this.radius + 1; // +1 because the "radius" of the mouse is 1

    if((dx * dx) + (dy * dy) > radii * radii) // is the mouse not over the player?
    {
        if((dx * dx) + (dy * dy) < 301 * 301)
        {
            this.x += ((Game.controls.mouseX - (this.x - xView)) * 2 / (this.mass)) + step;
            this.y += ((Game.controls.mouseY - (this.y - yView)) * 2 / (this.mass)) + step;
        }
        else
        {
            mx = Game.controls.mouseX;
            my = Game.controls.mouseY;

            do
            {
                dx = mx - (this.x - xView);
                dy = my - (this.y - yView);

                mx += (((this.x - xView) - mx) * 2 / (this.mass)) + step;
                my += (((this.y - yView) - my) * 2 / (this.mass)) + step;

            } while((dx * dx) + (dy * dy) > 301 * 301)

            this.x += ((mx - (this.x - xView)) * 2 / (this.mass)) + step;
            this.y += ((my - (this.y - yView)) * 2 / (this.mass)) + step;
        }
    }

The magic for 'outside the boundary' lies withing the do while. This is the best fix I could come up with and I cant see this as being an elegant or fast solution and am wondering what the proper course of action should be.

Im no artist but hopefully this image helps to illustrate what I am trying to achieve. The Black dot is the mouse pos, the black circle is the circle and the red circle is the boundary I have specified. I want to get the coords marked by the X. enter image description here

Rhexis
  • 2,414
  • 4
  • 28
  • 40
  • Just a side note which I forgot to mention, this is done entirely in javascript. – Rhexis Jun 20 '16 at 18:03
  • Do you need to find a coordinate of point at the circle of given radius (given center and mouse coordinate) ? – MBo Jun 20 '16 at 18:32

2 Answers2

3

Your question is a special case of Circle line-segment collision detection algorithm?, in this case with B and C being the same points, so you can use your center point for both of them.

That solution is given in C, but it translates to JavaScript very easily, just replace float with var, use Math.sqrt() and so on...

Oh, and then there is a JvaScript version here: Calculate the point of intersection of circle and line through the center, that's more appropriate :-)

Community
  • 1
  • 1
Jesper We
  • 5,977
  • 2
  • 26
  • 40
  • This looks like it could be perfect, I just need to modify it to be a point instead of a line right? (set the start and end of the line to the same value, in this case the mouse pos) – Rhexis Jun 20 '16 at 18:13
  • 1
    No you misunderstand. The endpoints of the line are the circle center and the mouse. Your cross is the intersection between this line and the red circle. – Jesper We Jun 20 '16 at 18:31
  • Ah right! My mistake, I understand now. I have it working too, thanks very much! Im a bit slow right now, its almost 5am. – Rhexis Jun 20 '16 at 18:44
0

If the black circle is in the center of the red circle and you have the radius of the red circle

// c is circle center
// mouse is the mouse position. Should have properties x,y
// radius is the circle radius;
// returns the point on the line where the circle intercepts it else it returns undefined.
function findX(c, mouse, radius)
    var v = {};
    // get the vector to the mouse
    v.x = mouse.x - c.x;
    v.y = mouse.y - c.y;
    var scale = radius / Math.hypot(v.x,v.y);
    if(scale < 1){ // is it outside the circle
        return {
            x : c.x + v.x * scale,
            y : c.y + v.y * scale
        };
    }
    return;
}

And if the the line start is not the center then a general purpose line circle intercept function will solve the problem. If the line starts inside the circle the function will return just one point. If the line is not long enough it will return an empty array..

// p1,p2 are the start and end points of a line
 // returns an array empty if no points found or one or two points depending on the number of intercepts found
 // If two points found the first point in the array is the point closest to the line start (p1)
 function circleLineIntercept(circle,radius,p1,p2){
        var v1 = {};
        var v2 = {};
        var ret = [];
        var u1,u2,b,c,d;
        // line as vector
        v1.x = p2.x - p1.x;
        v1.y = p2.y - p1.y;
        // vector to circle center
        v2.x = p1.x - circle.x;
        v2.y = p1.y - circle.y;
        // dot of line and circle
        b = (v1.x * v2.x + v1.y * v2.y) * -2;
        // length of line squared * 2
        c = 2 * (v1.x * v1.x + v1.y * v1.y);
        // some math to solve the two triangles made by the intercept points, the circle center and the perpendicular line to the line.
        d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - radius * radius));
        // will give a NaN if no solution
        if(isNaN(d)){ // no intercept
            return ret;
        }
        // get the unit distance of each intercept to the line
        u1 = (b - d) / c;
        u2 = (b + d) / c;

        // check the intercept is on the line segment
        if(u1 <= 1 && u1 >= 0){  
            ret.push({x:line.p1.x + v1.x * u1, y : line.p1.y + v1.y * u1 });
        }
        // check the intercept is on the line segment
        if(u2 <= 1 && u2 >= 0){  
            ret.push({x:line.p1.x + v1.x * u2, y : line.p1.y + v1.y * u2});
        }
        return ret;
    }
Blindman67
  • 51,134
  • 11
  • 73
  • 136