1
$\begingroup$

I am programmer by trade but am running into some trouble with a geometry problem.

I basically want to start at the center of a circle, travel any distance within the radius, turn any direction, and be able to determine how far I can move before reaching the perimeter of the circle.

I then want to move in that direction between 1 and max distance, stop, turn any direction, and determine the new "max distance" I can travel without exiting the circle.

Circle Known Variables

  • radius: 10 feet
  • distance traveled: 5 feet
  • origin 0,0 //center of circle
  • current position 0,5

Using Pythagorean theorem, I know that given above, if I am facing the top of the circle, and I turn 90 degrees left or right, the distance to the perimeter is 5^2 + b^2 = 10^2, so b = 8.6 feet. If I do a 180, there is 15 feet until the perimeter, and if I continue to look straight ahead, there is 5 feet.

I have used the above example to reverse engineer some code I have found.

Code found here: http://gmc.yoyogames.com/index.php?showtopic=576546

I hope someone can help me with the questions I have and point out my errors.

NOTE: You will notice I have covert to radians and back to degrees, and that is because c# functions require input in radians.

private static double getdist(double x, double y, double xx, double yy, double dir, double radius)
    {
        /*edge_distance(x,y,xx,yy,dir,Radius)
         *              0,0,0,5,90,10
         * 
        returns the distance from the point {xx,yy} to the edge of the circle with the centerpoint {x,y} and radius Radius in the direction dir
        {x,y} = the centerpoint of the circle
        {xx,yy} = the location of the point being tested
        dir = the direction to head to from the point towards the edge
        Radius = the radius of the circle.
        */

        double _x, _y, _xx, _yy, _dir, _B, _C, _A, _angle; //initialize temp variables

        _x = x;
        _y = y;
        _xx = xx;
        _yy = yy;
        _dir = dir;
        _B = radius;


        _A = Math.Sqrt(Math.Pow(_xx - _x, 2) + Math.Pow(_yy - _y, 2)); //find the distance from the center to the point

        if ((_x - _xx) != 0) //decide whether we are looking cos angle 1 or 2.
        {
            _A *= Math.Sign(_x - _xx);
        }

        if ((_y - _yy) != 0)//decide whether we are looking sin angle 1 or 2.
        {
            _A *= Math.Sign(_y - _yy);
        }

//original
_angle = 180 - Math.Abs(RadianToDegree(MathHelper.Arcsin((_A * (-Math.Sin(DegreeToRadian(_dir))) / _B)))); //find the missing angle.

//Change to cos
_angle = 180 - Math.Abs(RadianToDegree(MathHelper.Arccos((_A * (-Math.Cos(DegreeToRadian(_dir))) / _B)))); //find the missing angle.

        //reverse engineer
        //angle must = 60 for the math to work out at the end, so:
        //_angle = 180 - 120
        //RadianToDegree(MathHelper.Arcsin((5 * (-Math.Sin(DegreeToRadian(_dir))) / 10))) = 120
        //MathHelper.Arcsin((5 * (-Math.Sin(DegreeToRadian(_dir))) / 10)) = 2.09
        //(5 * (-Math.Sin(DegreeToRadian(_dir))/ 10) = .866
        //Math.Cos(DegreeToRadian(_dir)) =  1.732 //Sin cannot > 1
        //DegreeToRadian(_dir) = 0.5235987756
        //dir = 30
        //_angle = 180 - Math.Abs(RadianToDegree(MathHelper.Arcsin((5 * (-Math.Sin(DegreeToRadian(_dir))) / 10)))); //find the missing angle.

        _C = (Math.Pow(-_A, 2) + Math.Pow(_B, 2)) - (2 * _A * _B * Math.Cos(DegreeToRadian(_angle))); //find the distance squared

        //_C = (25 + 100) - (2 * -5 * 10 * -.5); //_angle=60

        return (Math.Sqrt(_C)); //return the distance.
    }

    private static double DegreeToRadian(double angle)
    {
        return Math.PI * angle / 180.0;
    }

    private static double RadianToDegree(double angle)
    {
        return angle * (180.0 / Math.PI);
    }

As I said, I reversed engineered based off the known variables above. I determined that _angle has to be 60 to return a distance of 8.6.

Of course now I know I have a 60 degree and 90 degree angle, so to complete the triangle it is 30 degrees.

The original code snippet had Math.Sin(DegreeToRadian(_dir)), and via reverse engineering I determined that value had to = 1.732. Since sin(x) cannot be > 1, I assume it was a mistake and changed it to Math.Cos(DegreeToRadian(_dir)) = 1.732. Cos (30) = 1.732 so that made sense. Let me know if this is incorrect.

To test, I pass in the knowns and loop through at 45 degree intervals. It doesn't reflect the answers I expected.

        double dist;

        for (int i = 0; i <= 360; i += 45)
        {
            dist = getdist(0, 0, 0, 5, i, 10);
            Console.WriteLine("i=" + i + ":" + dist);
        } 

Again, I assumed there would be a couple 8.66's, a 5, a 15, etc. I am not sure where I am going wrong.

Another question is how is direction perceived in a circle? Is 180 to the direct left, and 0/360 to the right? Or is 90/270 to the left right?

Thank you for your time and any help you can provide.

$\endgroup$
  • $\begingroup$ By convention, in mathematics, +x is right, +y is up, an angle of 0 goes along +x, and an angle of $90°=\pi/2$ radians goes along +y. A lot of computer programs (including SDL and Paint.net) have +y be down instead; this is more sensible for displaying graphics. Also you should probably get used to working in radians; they're more convenient in a lot of ways. $\endgroup$ – Dan Uznanski Aug 14 '14 at 2:46
  • $\begingroup$ Hi Dan, thanks for the help. I added a link to the original source code: gmc.yoyogames.com/index.php?showtopic=576546 $\endgroup$ – Chad Hensley Aug 14 '14 at 12:58
1
$\begingroup$

I haven't been able to follow the code, but here's how I'd do it.

given a direction angle $\theta$, you can find the unit vector that is the thing you multiply $t$ by to get a parametric form of the line: Specifically, given a point $P$ and $\theta$, the line has form $$A = P + t\left(\cos\theta, \sin\theta\right)$$ so $A$ is the point corresponding to a given $t$.

For convenience, I will define $\vec v = \left(\cos\theta, \sin\theta\right)$. I will use this a little later.

Second, given two points A and C, you can find the distance between them as $$d=\sqrt{\left(A_x-C_x\right)^2+\left(A_y-C_y\right)^2}$$ In this case, we want to find $A$ from the line's parametrization that has $d$ equal to the radius of the circle, given $C$ as the center of the circle.

This is a lot easier though if we square the whole thing; then we get a quadratic in t. $$d^2=\left(A_x-C_x\right)^2+\left(A_y-C_y\right)^2$$

Plug in the line's parametrization:

$$d^2=\left(P_x+t\cos\theta-C_x\right)^2+\left(P_y+t\sin\theta-C_y\right)^2$$ This we can simplify, and solve for t: this will be a quadratic.

$$d^2=P_x^2+2P_x\cos(\theta) t-2P_xC_x+t^2\cos^2\theta-2C_x\cos(\theta)t-C_x^2+P_y^2+2P_y\sin(\theta) t-2P_yC_y+t^2\sin^2\theta-2C_y\sin(\theta)t+C_y^2$$ $$=\left(P-C\right)^2+\left(2\left(P_x-C_x\right)\cos\theta+2\left(P_y-C_y\right)\sin\theta\right)t+t^2$$ $$t^2 + 2(P-C)\cdot \vec v t + (P-C)^2-d^2 = 0$$

Okay, enough mathematics, let's get down to brass tacks. Calculate the following:

$$\vec v = \left(\cos\theta, \sin\theta\right)$$ $$a=1$$ $$b=2(P-C)\cdot \vec v$$ $$c=(P-C)^2-d^2$$

Now we can plug these in to the quadratic formula.

$$t=\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$

I won't actually plug the values in, they're a little messy.

You'll get two values. For this task you'll want to find the positive value of $t$, and that's the distance (thanks to $\vec v$ being a unit vector), and if you want you can find the collision point by plugging that back into the line parametrization at the top.

For this task you'll need to be able to subtract, find the dot product of, and find the squared magnitude of vectors. Given vectors $(a_x, a_y)$ and $(b_x, b_y)$, $\vec a - \vec b = (a_x - b_x, a_y - b_y)$. $\vec a \cdot \vec b = a_xb_x+a_yb_y$. $\vec a^2 = \vec a\cdot \vec a$.

$\endgroup$
  • $\begingroup$ I am working through your math. I wasn't sure how to express P given my knowns. $\endgroup$ – Chad Hensley Aug 14 '14 at 17:54
  • $\begingroup$ P is, in your case, (0, 5); it's the start point of your ray. $\endgroup$ – Dan Uznanski Aug 14 '14 at 17:58
  • $\begingroup$ OK that is what I had thought, but it doesn't make sense to me: b = 2(0,5 - c)*v ? How is that expressed mathematically? Or is two separate equations? One for 0 and one for 5? $\endgroup$ – Chad Hensley Aug 14 '14 at 18:32
  • $\begingroup$ P and C are 2d vectors; in your case, C is (0,0). Subtract them using the formulas in the last paragraph; for this, if you don't have an existing 2d vector class (and if you're making a game, you really need one of these), you can use two assignments: PminusCx = Px - Cx; PminusCy = Py - Cy. $\endgroup$ – Dan Uznanski Aug 14 '14 at 18:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.