\$\begingroup\$
\$\endgroup\$
15
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.
diagram of shapes and points
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);
valueToAddToC0X = cosNegativeAngle*_bufferDistanceC0;
// Set P4's X coordinate
TrapezoidPoints.P4.X = _pointC0.X + valueToAddToC0X;
valueToAddToC1X = cosNegativeAngle*_bufferDistanceC1;
// Set P3's X coordinate
TrapezoidPoints.P3.X = _pointC1.X + valueToAddToC1X;
double sinNegativeAngle = Math.Sin(negativeAngle/RadiansToDegrees);
valueToAddToC0Y = sinNegativeAngle*_bufferDistanceC0;
// Set P4's Y coordinate
TrapezoidPoints.P4.Y = _pointC0.Y + valueToAddToC0Y;
valueToAddToC1Y = sinNegativeAngle*_bufferDistanceC1;
// 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 radius2 = radius0 - radius1;
double cosA = radius2/distance;
double angleAInRadians = Math.Acos(cosA);
double angleAInDegrees = angleAInRadians*RadiansToDegrees;
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
double angleInDegrees = angleInRadians*RadiansToDegrees;
// 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);
StonetipStonetip
-
\$\begingroup\$ Great question but pseudo code is off-topic \$\endgroup\$AD7six– AD7six2012年05月18日 10:59:52 +00:00Commented May 18, 2012 at 10:59
-
1\$\begingroup\$ 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. \$\endgroup\$Stonetip– Stonetip2012年05月18日 13:06:10 +00:00Commented May 18, 2012 at 13:06
-
\$\begingroup\$ 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. \$\endgroup\$AD7six– AD7six2012年05月18日 13:23:33 +00:00Commented May 18, 2012 at 13:23
-
1\$\begingroup\$ This problem is perfect for F#. I would start out by writing out the formulas in LaTeX. \$\endgroup\$Leonid– Leonid2012年05月18日 20:27:29 +00:00Commented May 18, 2012 at 20:27
-
1\$\begingroup\$ Uhm, what's the question being asked here? \$\endgroup\$miniBill– miniBill2012年07月11日 16:12:30 +00:00Commented Jul 11, 2012 at 16:12
1 Answer 1
\$\begingroup\$
\$\endgroup\$
- It's not clear to me what are
buffer0
andbuffer1
? If these represent the radius values (like C1-P1 line forbuffer1
), then perhapsradius0
(andradius1
) would be a better name here. - I'd create and use a data class for each point-radius pair. Say,
InputPoint
class withPoint
andRadius
or something similar. - 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 namedUri
- 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). SoTrapezoidPoints
should be the returned type of aGetTrapezoid
method (or maybeTryGetTrapezoid
if this pattern apply here), and not part of the class' state.
answered Oct 9, 2012 at 23:47
lang-cs