Circles of given radius through two points: Difference between revisions
(→{{header|Tcl}}: Marked incorrect as r==0 should not return circles.) |
(Added XPL0 example) |
||
Line 173: | Line 173: | ||
p1:(0.1234, 0.9876) p2:(0.1234, 0.9876) r:0.0 => |
p1:(0.1234, 0.9876) p2:(0.1234, 0.9876) r:0.0 => |
||
Circle:(0.1234, 0.9876, 0.0) |
Circle:(0.1234, 0.9876, 0.0) |
||
</pre> |
|||
=={{header|XPL0}}== |
|||
An easy way to solve this is to translate the coordinates so that one |
|||
point is at the origin. Then rotate the coordinate frame so that the |
|||
second point is on the X-axis. The circles' X coordinate is then half the |
|||
distance to the second point. The circles' Y coordinates are easily seen |
|||
as +/-sqrt(radius^2 - circleX^2). Now undo the rotation and translation. |
|||
The method used here is a streamlining of these steps. |
|||
<lang XPL0>include c:\cxpl\codes; |
|||
proc Circles; real Data; \Show centers of circles, given points P & Q and radius |
|||
real Px, Py, Qx, Qy, R, X, Y, X1, Y1, Bx, By, PB, CB; |
|||
[Px:= Data(0); Py:= Data(1); Qx:= Data(2); Qy:= Data(3); R:= Data(4); |
|||
if R = 0.0 then [Text(0, "Radius = zero gives no circles^M^J"); return]; |
|||
X:= (Qx-Px)/2.0; Y:= (Qy-Py)/2.0; |
|||
Bx:= Px+X; By:= Py+Y; |
|||
PB:= sqrt(X*X + Y*Y); |
|||
if PB = 0.0 then [Text(0, "Coincident points give infinite circles^M^J"); return]; |
|||
if PB > R then [Text(0, "Points are too far apart for radius^M^J"); return]; |
|||
CB:= sqrt(R*R - PB*PB); |
|||
X1:= Y*CB/PB; Y1:= X*CB/PB; |
|||
RlOut(0, Bx-X1); ChOut(0, ^,); RlOut(0, By+Y1); ChOut(0, 9\tab\); |
|||
RlOut(0, Bx+X1); ChOut(0, ^,); RlOut(0, By-Y1); CrLf(0); |
|||
]; |
|||
real Tbl; int I; |
|||
[Tbl:=[[0.1234, 0.9876, 0.8765, 0.2345, 2.0], |
|||
[0.0000, 2.0000, 0.0000, 0.0000, 1.0], |
|||
[0.1234, 0.9876, 0.1234, 0.9876, 2.0], |
|||
[0.1234, 0.9876, 0.8765, 0.2345, 0.5], |
|||
[0.1234, 0.9876, 0.1234, 0.9876, 0.0]]; |
|||
for I:= 0 to 4 do Circles(Tbl(I)); |
|||
]</lang> |
|||
{{out}} |
|||
<pre> |
|||
1.86311, 1.97421 -0.86321, -0.75211 |
|||
0.00000, 1.00000 0.00000, 1.00000 |
|||
Coincident points give infinite circles |
|||
Points are too far apart for radius |
|||
Radius = zero gives no circles |
|||
</pre> |
</pre> |
Revision as of 17:42, 17 April 2013
Given two points on a plane and a radius, usually two circles of given radius can be drawn through the points.
- Exceptions
- r==0.0 should be treated as never describing circles.
- If the points are coincident then an infinite number of circles with the point on their circumference can be drawn, unless r==0.0 as well which then collapses the circles to a point.
- If the points form a diameter then return two identical circles.
- If the points are too far apart then no circles can be drawn.
- Task detail
- Write a function/subroutine/method/... that takes two points and a radius and returns the two circles through those points, or some indication of special cases where two, possibly equal, circles cannot be returned.
- Show here the output for the following inputs:
p1 p2 r 0.1234, 0.9876 0.8765, 0.2345 2.0 0.0000, 2.0000 0.0000, 0.0000 1.0 0.1234, 0.9876 0.1234, 0.9876 2.0 0.1234, 0.9876 0.8765, 0.2345 0.5 0.1234, 0.9876 0.1234, 0.9876 0.0
- Ref
- Finding the Center of a Circle from 2 Points and Radius from Math forum @ Drexel
Python
The function raises the ValueError exception for the special cases and usess try - except to catch these and extract the exception detail.
<lang python>from collections import namedtuple from math import sqrt
Pt = namedtuple('Pt', 'x, y') Circle = Cir = namedtuple('Circle', 'x, y, r')
def circles_from_p1p2r(p1, p2, r):
'Following explanation at http://mathforum.org/library/drmath/view/53027.html' if r == 0.0: raise ValueError('radius of zero') (x1, y1), (x2, y2) = p1, p2 if p1 == p2: raise ValueError('coincident points gives infinite number of Circles') # delta x, delta y between points dx, dy = x2 - x1, y2 - y1 # dist between points q = sqrt(dx**2 + dy**2) if q > 2.0*r: raise ValueError('separation of points > diameter') # halfway point x3, y3 = (x1+x2)/2, (y1+y2)/2 # distance along the mirror line d = sqrt(r**2-(q/2)**2) # One answer c1 = Cir(x = x3 - d*dy/q, y = y3 + d*dx/q, r = abs(r)) # The other answer c2 = Cir(x = x3 + d*dy/q, y = y3 - d*dx/q, r = abs(r)) return c1, c2
if __name__ == '__main__':
for p1, p2, r in [(Pt(0.1234, 0.9876), Pt(0.8765, 0.2345), 2.0), (Pt(0.1234, 0.9876), Pt(0.1234, 0.9876), 2.0), (Pt(0.1234, 0.9876), Pt(0.8765, 0.2345), 0.5), (Pt(0.1234, 0.9876), Pt(0.1234, 0.9876), 0.0)]: print('Through points:\n %r,\n %r\n and radius %f\nYou can construct the following circles:' % (p1, p2, r)) try: print(' %r\n %r\n' % circles_from_p1p2r(p1, p2, r)) except ValueError as v: print(' ERROR: %s\n' % (v.args[0],))</lang>
- Output:
Through points: Pt(x=0.1234, y=0.9876), Pt(x=0.8765, y=0.2345) and radius 2.000000 You can construct the following circles: Circle(x=1.8631118016581893, y=1.974211801658189, r=2.0) Circle(x=-0.8632118016581896, y=-0.7521118016581892, r=2.0) Through points: Pt(x=0.0, y=2.0), Pt(x=0.0, y=0.0) and radius 1.000000 You can construct the following circles: Circle(x=0.0, y=1.0, r=1.0) Circle(x=0.0, y=1.0, r=1.0) Through points: Pt(x=0.1234, y=0.9876), Pt(x=0.1234, y=0.9876) and radius 2.000000 You can construct the following circles: ERROR: coincident points gives infinite number of Circles Through points: Pt(x=0.1234, y=0.9876), Pt(x=0.8765, y=0.2345) and radius 0.500000 You can construct the following circles: ERROR: separation of points > diameter Through points: Pt(x=0.1234, y=0.9876), Pt(x=0.1234, y=0.9876) and radius 0.000000 You can construct the following circles: ERROR: radius of zero
Tcl
<lang tcl>proc findCircles {p1 p2 r} {
lassign $p1 x1 y1 lassign $p2 x2 y2 # Special case: coincident & zero size if {$x1 == $x2 && $y1 == $y2 && $r == 0.0} {
return [list [list $x1 $y1 0.0]]
} if {$r <= 0.0} {
error "radius must be positive for sane results"
} if {$x1 == $x2 && $y1 == $y2} {
error "no sane solution: points are coincident"
}
# Calculate distance apart and separation vector set dx [expr {$x2 - $x1}] set dy [expr {$y2 - $y1}] set q [expr {hypot($dx, $dy)}] if {$q > 2*$r} {
error "no solution: points are further apart than required diameter"
}
# Calculate midpoint set x3 [expr {($x1+$x2)/2.0}] set y3 [expr {($y1+$y2)/2.0}] # Fractional distance along the mirror line set f [expr {($r**2 - ($q/2.0)**2)**0.5 / $q}] # The two answers set c1 [list [expr {$x3 - $f*$dy}] [expr {$y3 + $f*$dx}] $r] set c2 [list [expr {$x3 + $f*$dy}] [expr {$y3 - $f*$dx}] $r] return [list $c1 $c2]
}</lang> Demonstrating: <lang tcl>foreach {p1 p2 r} {
{0.1234 0.9876} {0.8765 0.2345} 2.0 {0.0000 2.0000} {0.0000 0.0000} 1.0 {0.1234 0.9876} {0.1234 0.9876} 2.0 {0.1234 0.9876} {0.8765 0.2345} 0.5 {0.1234 0.9876} {0.1234 0.9876} 0.0
} {
puts "p1:([join $p1 {, }]) p2:([join $p2 {, }]) r:$r =>" if {[catch {
foreach c [findCircles $p1 $p2 $r] { puts "\tCircle:([join $c {, }])" }
} msg]} {
puts "\tERROR: $msg"
}
}</lang>
- Output:
p1:(0.1234, 0.9876) p2:(0.8765, 0.2345) r:2.0 => Circle:(1.863111801658189, 1.974211801658189, 2.0) Circle:(-0.8632118016581891, -0.752111801658189, 2.0) p1:(0.0000, 2.0000) p2:(0.0000, 0.0000) r:1.0 => Circle:(0.0, 1.0, 1.0) Circle:(0.0, 1.0, 1.0) p1:(0.1234, 0.9876) p2:(0.1234, 0.9876) r:2.0 => ERROR: no sane solution: points are coincident p1:(0.1234, 0.9876) p2:(0.8765, 0.2345) r:0.5 => ERROR: no solution: points are further apart than required diameter p1:(0.1234, 0.9876) p2:(0.1234, 0.9876) r:0.0 => Circle:(0.1234, 0.9876, 0.0)
XPL0
An easy way to solve this is to translate the coordinates so that one point is at the origin. Then rotate the coordinate frame so that the second point is on the X-axis. The circles' X coordinate is then half the distance to the second point. The circles' Y coordinates are easily seen as +/-sqrt(radius^2 - circleX^2). Now undo the rotation and translation. The method used here is a streamlining of these steps.
<lang XPL0>include c:\cxpl\codes;
proc Circles; real Data; \Show centers of circles, given points P & Q and radius real Px, Py, Qx, Qy, R, X, Y, X1, Y1, Bx, By, PB, CB; [Px:= Data(0); Py:= Data(1); Qx:= Data(2); Qy:= Data(3); R:= Data(4); if R = 0.0 then [Text(0, "Radius = zero gives no circles^M^J"); return]; X:= (Qx-Px)/2.0; Y:= (Qy-Py)/2.0; Bx:= Px+X; By:= Py+Y; PB:= sqrt(X*X + Y*Y); if PB = 0.0 then [Text(0, "Coincident points give infinite circles^M^J"); return]; if PB > R then [Text(0, "Points are too far apart for radius^M^J"); return]; CB:= sqrt(R*R - PB*PB); X1:= Y*CB/PB; Y1:= X*CB/PB; RlOut(0, Bx-X1); ChOut(0, ^,); RlOut(0, By+Y1); ChOut(0, 9\tab\); RlOut(0, Bx+X1); ChOut(0, ^,); RlOut(0, By-Y1); CrLf(0); ];
real Tbl; int I; [Tbl:=[[0.1234, 0.9876, 0.8765, 0.2345, 2.0],
[0.0000, 2.0000, 0.0000, 0.0000, 1.0], [0.1234, 0.9876, 0.1234, 0.9876, 2.0], [0.1234, 0.9876, 0.8765, 0.2345, 0.5], [0.1234, 0.9876, 0.1234, 0.9876, 0.0]];
for I:= 0 to 4 do Circles(Tbl(I)); ]</lang>
- Output:
1.86311, 1.97421 -0.86321, -0.75211 0.00000, 1.00000 0.00000, 1.00000 Coincident points give infinite circles Points are too far apart for radius Radius = zero gives no circles