uray
uray

Reputation: 11562

find tangent line of two adjacent circle

enter image description here

those are 2 example cases of what I need to solve, it is just finding the coordinate of D, given position of A, and the direction vector of red and green line

much help and hint appreciated, preferred in some code instead of math equation

Upvotes: 0

Views: 172

Answers (4)

uray
uray

Reputation: 11562

thx for all the answer (will upvote them), I did the work on it myself, I will share my result :

enter image description here

and here is length of d, to solve the equation :

enter image description here

Upvotes: 0

John Alexiou
John Alexiou

Reputation: 29244

You know where A is and the angle θ it makes from vertical.

fig1

So specify the line though C called line(C) above and the offset the line by R in order to get line(D) above that goes through point D.

In C# code this is

Line line_C = Line.ThroughPointAtAngle(A, theta);
Line line_D = line_C.Offset(radius);

Now find the intersection of this line to the greater circle

Circle circle = new Circle(B, 2 * radius);
if (circle.Intersect(line_D, out Point D, alternate: false))
{
    Console.WriteLine(D);
    float d_BD = B.DistanceTo(D);
    Console.WriteLine(d_BD);
}
else
{
    Console.WriteLine("Does not intersect.");
}

This produces point D either above line(C) or below line(C) depending on the bool argument alternate.


The code example below produces the following output:

D=Point(-0.4846499,-1.94039)
|BD|=2

The source code is

Program.cs

using static Float;

static class Program
{
    static void Main(string[] args)
    {
        float radius = 1;

        Point A = new Point(-radius, 0);
        Point B = new Point(0, 0);

        float theta = deg(15);

        Line line_C = Line.ThroughPointAtAngle(A, theta);
        Line line_D = line_C.Offset(radius);

        Circle circle = new Circle(B, 2 * radius);
        if (circle.Intersect(line_D, out Point D, alternate: false))
        {
            Console.WriteLine($"D={D}");
            float d_BD = B.DistanceTo(D);
            Console.WriteLine($"|BD|={d_BD}");
        }
        else
        {
            Console.WriteLine("Does not intersect.");
        }
    }
}

Point.cs

Describes a point in cartesian space using two coordinates (x,y)

using static Float;

public readonly struct Point
{
    readonly (float x, float y) data;

    public Point(float x, float y)
    {
        this.data = (x, y);
    }
    public static Point Origin { get; } = new Point(0, 0);
    public static Point FromTwoLines(Line line1, Line line2)
    {
        float x = line1.B * line2.C - line1.C * line2.B;
        float y = line1.C * line2.A - line1.A * line2.C;
        float w = line1.A * line2.B - line1.B * line2.A;
        return new Point(x / w, y / w);            
    }
    public float X => data.x;
    public float Y => data.y;

    public float SumSquares => data.x * data.x + data.y * data.y;

    #region Algebra
    public static Point Negate(Point a)
        => new Point(
            -a.data.x,
            -a.data.y);
    public static Point Scale(float factor, Point a)
        => new Point(
            factor * a.data.x,
            factor * a.data.y);
    public static Point Add(Point a, Point b)
        => new Point(
            a.data.x + b.data.x,
            a.data.y + b.data.y);
    public static Point Subtract(Point a, Point b)
        => new Point(
            a.data.x - b.data.x,
            a.data.y - b.data.y);

    public static float Dot(Point point, Line line)
        => line.A * point.data.x + line.B * point.data.y + line.C;

    public static Point operator +(Point a, Point b) => Add(a, b);
    public static Point operator -(Point a) => Negate(a);
    public static Point operator -(Point a, Point b) => Subtract(a, b);
    public static Point operator *(float f, Point a) => Scale(f, a);
    public static Point operator *(Point a, float f) => Scale(f, a);
    public static Point operator /(Point a, float d) => Scale(1 / d, a);
    #endregion

    #region Geometry

    public Point Offset(float dx, float dy)
        => new Point(data.x + dx, data.y + dy);
    public Point Offset(Vector2 delta) => Offset(delta.X, delta.Y);

    public float DistanceTo(Point point)
        => sqrt(sqr(data.x - point.data.x) + sqr(data.y - point.data.y));
    #endregion

    #region Formatting
    public string ToString(string formatting, IFormatProvider provider)
    {
        return $"Point({data.x.ToString(formatting, provider)},{data.y.ToString(formatting, provider)})";
    }
    public string ToString(string formatting)
        => ToString(formatting, null);
    public override string ToString()
        => ToString("g"); 
    #endregion

}

Line.cs

Describes a line in cartesian space using the coefficients (a,b,c) such that the equation of the line is a x + b y + c = 0

using static Float;

public readonly struct Line
{
    readonly (float a, float b, float c) data;

    public Line(float a, float b, float c) : this()
    {
        data = (a, b, c);
    }

    public static Line AlongX { get; } = new Line(0, 1, 0);
    public static Line AlongY { get; } = new Line(-1, 0, 0);

    public static Line ThroughPointAtAngle(Point point, float angle)
    {
        return new Line(cos(angle), -sin(angle), point.Y * sin(angle) - point.X * cos(angle));
    }
    public static Line ThroughTwoPoints(Point point1, Point point2)
        => new Line(
            point1.Y - point2.Y,
            point2.X - point1.X,
            point1.X * point2.Y - point1.Y * point2.X);

    public float A => data.a;
    public float B => data.b;
    public float C => data.c;

    #region Algebra

    public static float Dot(Line line, Point point)
        => line.data.a * point.X + line.data.b * point.Y + line.data.c;

    #endregion

    #region Geometry
    public Line ParallelThrough(Point point)
    {
        return new Line(data.a, data.b, -data.a * point.X - data.b * point.Y);
    }
    public Line PerpendicularThrough(Point point)
    {
        return new Line(data.b, -data.a, -data.b * point.X + data.a * point.Y);
    }
    public Line Offset(float amount)
        => new Line(data.a, data.b, data.c - amount * sqrt(sqr(data.a) + sqr(data.b)));

    public Line Offset(float dx, float dy)
        => new Line(data.a, data.b, data.c + data.a * dx + data.b * dy);

    public Line Offset(Vector2 delta) => Offset(delta.X, delta.Y);

    public float DistanceTo(Point point)
        => Dot(this, point) / (data.a * data.a + data.b * data.b);
    #endregion

    #region Formatting
    public string ToString(string formatting, IFormatProvider provider)
    {
        return $"Line({data.a.ToString(formatting, provider)}x+{data.b.ToString(formatting, provider)}y+{data.c.ToString(formatting, provider)}=0)";
    }
    public string ToString(string formatting)
        => ToString(formatting, null);
    public override string ToString()
        => ToString("g"); 
    #endregion
}

Circle.cs

Describes a circle using the center and radius.

using static Float;

public readonly struct Circle
{
    readonly (Point center, float radius) data;

    public Circle(Point center, float radius)
    {
        this.data = (center, radius);
    }

    public static Circle FromTwoPoints(Point point1, Point point2)
    {
        float radius = point1.DistanceTo(point2) / 2;
        Point center = (point1 + point2) / 2;
        return new Circle(center, radius);
    }

    public static Circle FromThreePoints(Point point1, Point point2, Point point3)
    {
        float k_1 = point1.SumSquares / 2;
        float k_2 = point2.SumSquares / 2;
        float k_3 = point3.SumSquares / 2;

        float dx_12 = point2.X - point1.X;
        float dy_12 = point2.Y - point1.Y;
        float dx_23 = point3.X - point2.X;
        float dy_23 = point3.Y - point2.Y;

        float det = dx_12 * dy_23 - dx_23 * dy_12;

        Point center = new Point(
            (dy_12 * (k_2 - k_3) + dy_23 * (k_2 - k_1)) / det,
            (dx_12 * (k_3 - k_2) + dx_23 * (k_1 - k_2)) / det);
        float radius = center.DistanceTo(point1);

        return new Circle(center, radius);
    }

    public Point Center => data.center;
    public float Radius => data.radius;

    #region Geometry

    public float DistanceTo(Point point)
        => data.center.DistanceTo(point) - data.radius;

    public float DistanceTo(Line line)
    {
        float d = line.DistanceTo(Center);
        if (d > 0)
        {
            return d - data.radius;
        }
        else
        {
            return d + data.radius;
        }
    }

    public bool Intersect(Line line, out Point point, bool alternate = false)
    {
        line = line.Offset(-Center.X, -Center.Y);
        int sign = alternate ? -1 : 1;
        float discr = sqr(line.A * data.radius) + sqr(line.B * data.radius) - sqr(line.C);
        if (discr >= 0)
        {
            float d = sign * sqrt(discr);
            float ab = line.A * line.A + line.B * line.B;
            point = new Point((line.B * d - line.A * line.C) / ab, -(line.A * d + line.B * line.C) / ab);
            point += Center;
            return true;
        }
        else
        {
            float ab = line.A * line.A + line.B * line.B;
            point = new Point((-line.A * line.C) / ab, -(+line.B * line.C) / ab);
            point += Center;
            return false;
        }

    }

    #endregion

    #region Formatting
    public string ToString(string formatting, IFormatProvider provider)
    {
        return $"Circle({data.center.ToString(formatting, provider)},{data.radius.ToString(formatting, provider)})";
    }
    public string ToString(string formatting)
        => ToString(formatting, null);
    public override string ToString()
        => ToString("g"); 
    #endregion

}

Float.cs

Helper functions dealing with float math which is lacking from System.Math.

public static class Float
{
    /// <summary>
    /// A factor of π.
    /// </summary>
    /// <param name="x">The factor.</param>
    public static float pi(float x) => (float)(Math.PI * x);
    /// <summary>
    /// Degree to Radian conversion
    /// </summary>
    /// <param name="x">The angle in degrees.</param>
    /// <returns>Angle in radians</returns>
    public static float deg(float x) => pi(x) / 180;
    /// <summary>
    /// Radian to Degree conversion
    /// </summary>
    /// <param name="x">The angle in radians.</param>
    /// <returns>Angle in degrees</returns>
    public static float rad(float x) => x * 180 / pi(1);
    public static float sqr(float x) => x * x;

    public static float sqrt(float x) => (float)Math.Sqrt(x);
    public static float sin(float x) => (float)Math.Sin(x);
    public static float cos(float x) => (float)Math.Cos(x);
}

Upvotes: 1

As a hint: draw it out some more:

enter image description here

We can construct D by constructing the line segment AG, for which we know both the angle and length, because AC⟂AG, and the segment has length R.

We can then construct a line perpendicular to AG, through G, which gives us a chord on the blue circle, one endpoint of which is D. We know the distance from B to GD (because we know trigonometry) and we know that the distance BD is 2R (because that's a given). Pythagoras then trivially gives us D.

Upvotes: 2

MBo
MBo

Reputation: 80187

Having coordinates A,B,C, we can write two vector equations using scalar (dot) product:

AC.dot.DC = 0
DB.dot.DB = 4*R^2

The first one refers to perpendicularity between tangent to circle and radius to tangency point, the second one - just squared distance between circle centers.

In coordinates:

(cx-ax)*(cx-dx) + (cy-ay)*(cy-dy) = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2

Solve this system for unknown dx, dy - two solutions in general case.

If A and C are not known, as @Mike 'Pomax' Kamermans noticed:

Let

cr = sin(v)     sr = cos(v)
cg = sin(w)     sg = cos(w)

So

ax = bx + R * cr
ay = by + R * sr

and

dx = cx - R * cg
dy = cy + R * sg

Substituting expressions into the system above we have:

(dx+R*cg-bx-R*cr)*cg - (dy-R*sg-by-R*sr)*sg = 0
(bx-dx)*(bx-dx) + (by-dy)*(by-dy) = 4*R^2

Again - solve system for unknowns dx, dy

Upvotes: 2

Related Questions