# C# code to derive tangential points between two circles to create a trapezoid

These are the steps to determine coordinates of the 4 points (P1, P2, P3, P4) that make up a tangential trapezoid connecting to circles. Another way of looking at it is to think of the tangential segments of being the parts of a belt that would not be wrapped around the pulleys. The math should work regardless of the orientation of the two circles in coordinate space.

Code (usage @ bottom):

``````internal class TrapezoidBuilder
{
private const double RadiansToDegrees = 180/Math.PI;
private readonly double _bufferDistanceC0;
private readonly double _bufferDistanceC1;

private readonly Point _pointC0;
private readonly Point _pointC1;
public TrapezoidPoints TrapezoidPoints;

public TrapezoidBuilder(Point pointC0, Point pointC1, double bufferDistanceC0, double bufferDistanceC1)
{
_pointC0 = pointC0;
_pointC1 = pointC1;
_bufferDistanceC0 = bufferDistanceC0;
_bufferDistanceC1 = bufferDistanceC1;

TrapezoidPoints = new TrapezoidPoints();

CalculateTrapezoidPoints();
}

public void CalculateTrapezoidPoints()
{
// Get the angle of the line C0-C1 in degrees. This will be used in conjunction with angleA to determine the vector of these points
double angleRelativeToPositiveXAxis = CalculateAngleRelativeToXAxis(_pointC0, _pointC1);

// Get angleA
double angleA = CalculateAngleA(_pointC0, _pointC1, _bufferDistanceC0, _bufferDistanceC1);

////  Calculate P1 and P2 coordinates first

double positiveAngle = angleRelativeToPositiveXAxis + angleA;

double cosPositiveAngle = Math.Cos(positiveAngle/RadiansToDegrees);

double valueToAddToC0X = cosPositiveAngle*_bufferDistanceC0;

// Set P1's X coordinate
TrapezoidPoints.P1.X = _pointC0.X + valueToAddToC0X;

double valueToAddToC1X = cosPositiveAngle*_bufferDistanceC1;

// Set P2's X coordinate
TrapezoidPoints.P2.X = _pointC1.X + valueToAddToC1X;

double sinPositiveAngle = Math.Sin(positiveAngle/RadiansToDegrees);

double valueToAddToC0Y = sinPositiveAngle*_bufferDistanceC0;

// Set P1's Y coordinate
TrapezoidPoints.P1.Y = _pointC0.Y + valueToAddToC0Y;

double valueToAddToC1Y = sinPositiveAngle*_bufferDistanceC1;

// Set P2's Y coordinate
TrapezoidPoints.P2.Y = _pointC1.Y + valueToAddToC1Y;

////  Calculate P3 and P4 coordinates

double negativeAngle = angleRelativeToPositiveXAxis - angleA;

double cosNegativeAngle = Math.Cos(negativeAngle/RadiansToDegrees);

// Set P4's X coordinate
TrapezoidPoints.P4.X = _pointC0.X + valueToAddToC0X;

// Set P3's X coordinate
TrapezoidPoints.P3.X = _pointC1.X + valueToAddToC1X;

double sinNegativeAngle = Math.Sin(negativeAngle/RadiansToDegrees);

// Set P4's Y coordinate
TrapezoidPoints.P4.Y = _pointC0.Y + valueToAddToC0Y;

// Set P3's Y coordinate
TrapezoidPoints.P3.Y = _pointC1.Y + valueToAddToC1Y;

Debug.WriteLine("C0   " + _pointC0.X + "   " + _pointC0.Y);
Debug.WriteLine("C1   " + _pointC1.X + "   " + _pointC1.Y);

Debug.WriteLine("P1   " + TrapezoidPoints.P1.X + "   " + TrapezoidPoints.P1.Y);
Debug.WriteLine("P2   " + TrapezoidPoints.P2.X + "   " + TrapezoidPoints.P2.Y);
Debug.WriteLine("P3   " + TrapezoidPoints.P3.X + "   " + TrapezoidPoints.P3.Y);
Debug.WriteLine("P4   " + TrapezoidPoints.P4.X + "   " + TrapezoidPoints.P4.Y);
}

private double CalculateAngleA(Point pointC0, Point pointC1, double radius0, double radius1)
{
double xDistance = pointC1.X - pointC0.X;
double yDistance = pointC1.Y - pointC0.Y;

double distance = Math.Sqrt((xDistance*xDistance) + (yDistance*yDistance));

double cosA = radius2/distance;

double angleAInRadians = Math.Acos(cosA);

return angleAInDegrees;
}

private double CalculateAngleRelativeToXAxis(Point point0, Point point1)
{
try
{
// In order to use ATAN2, point C1 has to be considered as the origin, i.e. 0, 0.
// So C1x is subtracted from C2x and C1y from C2y. Note that it’s important to subtract
// the 1st value from the 2nd to help determine which quadrant the angle is in.
double x = point1.X - point0.X;
double y = point1.Y - point0.Y;

// Get the angle in radians
double angleInRadians = Math.Atan2(x, y);

// Convert to degrees

// Subtract from 90 to get the angle relative to the positive X-axis
double relativeAngleInDegrees = 90 - angleInDegrees;
// Return result
return relativeAngleInDegrees;
}
catch (Exception err)
{
Debug.WriteLine(err.Message);
}

// If no result, return zero
return 0;
}
}

internal class TrapezoidPoints
{
public Point P1;
public Point P2;
public Point P3;
public Point P4;
}

// Usage
Point C1 = new Point(5,7);
Point C2 = new Point(6.516, 7.875);
double buffer0 = 1;
double buffer1 = .375;

var trapezoidBuilder = new TrapezoidBuilder(C1, C2, buffer0, buffer1);
``````
-
Great question but pseudo code is off-topic –  AD7six May 18 '12 at 10:59
With all due respect (and I should have read the rules first), this policy isn't helpful. While I'm capable of posting actual code, it will become very specific (harder to wade through), will not provide a concrete example and would result in wasted effort if my general approach is wrong. –  Stonetip May 18 '12 at 13:06
I'm just a member, but pseudo code has one huge disadvantage - you can't check if the code actually works or run it to ensure the changes you proposed would actually work. –  AD7six May 18 '12 at 13:23
This problem is perfect for F#. I would start out by writing out the formulas in LaTeX. –  Leonid May 18 '12 at 20:27
Uhm, what's the question being asked here? –  miniBill Jul 11 '12 at 16:12
show 10 more comments

1. It's not clear to me what are `buffer0` and `buffer1`? If these represent the radius values (like C1-P1 line for `buffer1`), then perhaps `radius0` (and `radius1`) would be a better name here.
2. I'd create and use a data class for each point-radius pair. Say, `InputPoint` class with `Point` and `Radius` or something similar.
3. I'd separate constructor and results. The results are calculations that should not be part of the constructor. Take, for example, the class named `UriBuilder` in .NET: you can construct it, change the inputs, and only when you call the property named `Uri` - you get the calculated uri. The same should apply here, too. Constructing a class gives us an instance with a valid state. Calculations - in their own methods or property-getters (that are practically methods, by the way). So `TrapezoidPoints` should be the returned type of a `GetTrapezoid` method (or maybe `TryGetTrapezoid` if this pattern apply here), and not part of the class' state.