Circles of given radius through two points: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|REXX}}: added the REXX language. -- ~~~~)
(→‎{{header|REXX}}: moved "pre" statement. -- ~~~~)
Line 211: Line 211:


=={{header|REXX}}==
=={{header|REXX}}==
{{trans|xxx}}
{{trans|XPL0}}
<lang rexx>/*REXX pgm finds 2 circles with a specific radius given two (X,Y) points*/
<lang rexx>/*REXX pgm finds 2 circles with a specific radius given two (X,Y) points*/
@. =
@. =
Line 245: Line 245:
return f(bx-x1) f(by+y1) f(bx+x1) f(by-y1)</lang>
return f(bx-x1) f(by+y1) f(bx+x1) f(by-y1)</lang>
'''output''' when using the default input(s):
'''output''' when using the default input(s):
<pre style="overflow:scroll">
x1 y1 x2 y2 radius cir1x cir1y cir2x cir2y
x1 y1 x2 y2 radius cir1x cir1y cir2x cir2y
──────── ──────── ──────── ──────── ────── ──────── ──────── ──────── ────────
──────── ──────── ──────── ──────── ────── ──────── ──────── ──────── ────────
Line 252: Line 253:
0.1234 0.9876 0.8765 0.2345 0.5 ───► points are too far apart for the given radius
0.1234 0.9876 0.8765 0.2345 0.5 ───► points are too far apart for the given radius
0.1234 0.9876 0.1234 0.9876 0 ───► radius of zero gives no circles.
0.1234 0.9876 0.1234 0.9876 0 ───► radius of zero gives no circles.
<pre style="overflow:scroll">
</pre>
</pre>



=={{header|Tcl}}==
=={{header|Tcl}}==

Revision as of 06:27, 20 April 2013

Circles of given radius through two points is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Given two points on a plane and a radius, usually two circles of given radius can be drawn through the points.

Exceptions
  1. r==0.0 should be treated as never describing circles.
  2. 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.
  3. If the points form a diameter then return two identical circles.
  4. 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

D

Translation of: Python

<lang d>import std.stdio, std.typecons, std.math;

class ValueException : Exception {

   this(string msg_) { super(msg_); }

}

struct V2 { double x, y; } struct Circle { double x, y, r; }

/**Following explanation at: http://mathforum.org/library/drmath/view/53027.html

  • /

Tuple!(Circle, Circle) circlesFromTwoPointsAndRadius(in V2 p1, in V2 p2, in double r) pure in {

   assert(r >= 0, "radius can't be negative");

} body {

   enum nBits = 40;
   if (r.abs < (1.0 / (2.0 ^^ nBits)))
       throw new ValueException("radius of zero");
   if (feqrel(cast()p1.x, cast()p2.x) >= nBits &&
       feqrel(cast()p1.y, cast()p2.y) >= nBits)
       throw new ValueException("coincident points give" ~
                                " infinite number of Circles");
   // Delta x, delta y between points.
   immutable dx = p2.x - p1.x;
   immutable dy = p2.y - p1.y;
   // Dist between points.
   immutable q = sqrt(dx ^^ 2 + dy ^^ 2);
   if (q > 2.0 * r)
       throw new ValueException("separation of points > diameter");
   // Halfway point.
   immutable x3 = (p1.x + p2.x) / 2;
   immutable y3 = (p1.y + p2.y) / 2;
   // Distance along the mirror line.
   immutable d = sqrt(r ^^ 2 - (q / 2) ^^ 2);
   immutable c1 = Circle(x3 - d * dy / q, y3 + d * dx / q, r.abs);
   immutable c2 = Circle(x3 + d * dy / q, y3 - d * dx / q, r.abs);
   return typeof(return)(c1, c2);

}

void main() {

   foreach (t; [tuple(V2(0.1234, 0.9876), V2(0.8765, 0.2345), 2.0),
                tuple(V2(0.0000, 2.0000), V2(0.0000, 0.0000), 1.0),
                tuple(V2(0.1234, 0.9876), V2(0.1234, 0.9876), 2.0),
                tuple(V2(0.1234, 0.9876), V2(0.8765, 0.2345), 0.5),
                tuple(V2(0.1234, 0.9876), V2(0.1234, 0.9876), 0.0)]) {
       writefln("Through points:\n  %s,\n  %s\n  and radius %f\n" ~
                "You can construct the following circles:", t[]);
       try {
           writefln("  %s\n  %s\n",
                    circlesFromTwoPointsAndRadius(t[])[]);
       } catch (ValueException v)
           writefln("  ERROR: %s\n", v.msg);
   }

}</lang>

Output:
Through points:
  V2(0.1234, 0.9876),
  V2(0.8765, 0.2345)
  and radius 2.000000
You can construct the following circles:
  Circle(1.86311, 1.97421, 2)
  Circle(-0.863212, -0.752112, 2)

Through points:
  V2(0, 2),
  V2(0, 0)
  and radius 1.000000
You can construct the following circles:
  Circle(0, 1, 1)
  Circle(0, 1, 1)

Through points:
  V2(0.1234, 0.9876),
  V2(0.1234, 0.9876)
  and radius 2.000000
You can construct the following circles:
  ERROR: coincident points give infinite number of Circles

Through points:
  V2(0.1234, 0.9876),
  V2(0.8765, 0.2345)
  and radius 0.500000
You can construct the following circles:
  ERROR: separation of points > diameter

Through points:
  V2(0.1234, 0.9876),
  V2(0.1234, 0.9876)
  and radius 0.000000
You can construct the following circles:
  ERROR: radius of zero

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

REXX

Translation of: XPL0

<lang rexx>/*REXX pgm finds 2 circles with a specific radius given two (X,Y) points*/ @. = @.1= 0.1234 0.9876 0.8765 0.2345 2 @.2= 0.0000 2.0000 0.0000 0.0000 1 @.3= 0.1234 0.9876 0.1234 0.9876 2 @.4= 0.1234 0.9876 0.8765 0.2345 0.5 @.5= 0.1234 0.9876 0.1234 0.9876 0 say ' x1 y1 x2 y2 radius cir1x cir1y cir2x cir2y' say ' ──────── ──────── ──────── ──────── ────── ──────── ──────── ──────── ────────'

     do  j=1  while  @.j\==         /*process all given points&radius*/
        do k=1  for 4;   w.k=f(word(@.j,k));  end /*k*/   /*format num.*/
     say w.1 w.2 w.3 w.4 center(word(@.j,5),9)  "───► "  twoCircles(@.j)
     end           /*j*/

exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────F subroutine────────────────────────*/ f: return right(format(arg(1),,4),9) /*format a number with 4 dec dig.*/ /*──────────────────────────────────SQRT subroutine─────────────────────*/ sqrt: procedure; parse arg x; if x=0 then return 0; d=digits();numeric digits 11

     g=.sqrtGuess();       do j=0 while p>9;  m.j=p;  p=p%2+1;   end
     do k=j+5 to 0 by -1; if m.k>11 then numeric digits m.k; g=.5*(g+x/g); end
     numeric digits d;  return g/1

.sqrtGuess: if x<0 then call sqrtErr; numeric form; m.=11; p=d+d%4+2

     parse value format(x,2,1,,0) 'E0' with g 'E' _ .;   return g*.5'E'_%2

/*──────────────────────────────────twoCircles subroutine───────────────*/ twoCircles: procedure; parse arg px py qx qy r . if r=0 then return 'radius of zero gives no circles.' x=(qx-px)/2; y=(qy-py)/2; bx=px+x; by=py+y; pb=sqrt(x**2+y**2) if pb=0 then return 'coincident points give infinite circles' if pb>r then return 'points are too far apart for the given radius' cb=sqrt(r**2-pb**2); x1=y*cb/pb; y1=x*cb/pb return f(bx-x1) f(by+y1) f(bx+x1) f(by-y1)</lang> output when using the default input(s):

     x1        y1        x2        y2     radius            cir1x     cir1y     cir2x     cir2y
  ────────  ────────  ────────  ────────  ──────          ────────  ────────  ────────  ────────
   0.1234    0.9876    0.8765    0.2345     2     ───►     1.8631    1.9742   -0.8632   -0.7521
   0.0000    2.0000    0.0000    0.0000     1     ───►     0.0000    1.0000    0.0000    1.0000
   0.1234    0.9876    0.1234    0.9876     2     ───►  coincident points give infinite circles
   0.1234    0.9876    0.8765    0.2345    0.5    ───►  points are too far apart for the given radius
   0.1234    0.9876    0.1234    0.9876     0     ───►  radius of zero gives no circles.

Tcl

This example is incorrect. It does not accomplish the given task. Please fix the code and remove this message.
Translation of: Python

<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