I have a pair of points and I would like to find a circles of known r that are determined by these two points. I will be using this in a simulation and possible space for x
and y
have boundaries (say a box of -200, 200).
It is known that square of radius is
(x-x1)^2 + (y-y1)^2 = r^2
(x-x2)^2 + (y-y2)^2 = r^2
I would now like to solve this non-linear system of equations to get two potential circle centers. I tried using package BB
. Here is my feeble attempt which gives only one point. What I would like to get is both possible points. Any pointers into right direction will be met with complimentary beer on first possible occasion.
library(BB)
known.pair <- structure(c(-46.9531139599816, -62.1874917150412, 25.9011462171242,
16.7441676243879), .Dim = c(2L, 2L), .Dimnames = list(NULL, c("x",
"y")))
getPoints <- function(ps, r, tr) {
# get parameters
x <- ps[1]
y <- ps[2]
# known coordinates of two points
x1 <- tr[1, 1]
y1 <- tr[1, 2]
x2 <- tr[2, 1]
y2 <- tr[2, 2]
out <- rep(NA, 2)
out[1] <- (x-x1)^2 + (y-y1)^2 - r^2
out[2] <- (x-x2)^2 + (y-y2)^2 - r^2
out
}
slvd <- BBsolve(par = c(0, 0),
fn = getPoints,
method = "L-BFGS-B",
tr = known.pair,
r = 40
)
Graphically you can see this with the following code, but you will need some extra packages.
library(sp)
library(rgeos)
plot(0,0, xlim = c(-200, 200), ylim = c(-200, 200), type = "n", asp = 1)
points(known.pair)
found.pt <- SpatialPoints(matrix(slvd$par, nrow = 1))
plot(gBuffer(found.pt, width = 40), add = T)
ADDENDUM
Thank you all for your valuable comments and code. I provide timings for answers by posters who complimented their answers with code.
test replications elapsed relative user.self sys.self user.child sys.child
4 alex 100 0.00 NA 0.00 0 NA NA
2 dason 100 0.01 NA 0.02 0 NA NA
3 josh 100 0.01 NA 0.02 0 NA NA
1 roland 100 0.15 NA 0.14 0 NA NA
In order to find the center and radius, we need to change the equation of the circle into standard form, ( x − h ) 2 + ( y − k ) 2 = r 2 (x-h)^2+(y-k)^2=r^2 (x−h)2+(y−k)2=r2, where h and k are the coordinates of the center and r is the radius.
The following code will get you the points at the centers of the two desired circles. No time right now to comment this up or convert the results to Spatial*
objects, but this should give you a good start.
First, here's an ASCII-art diagram to introduce point names. k
and K
are the known points, B
is a point on the horizontal drawn through k
, and C1
and C2
are the centers of the circles you are after:
C2
K
k----------------------B
C1
Now the code:
# Example inputs
r <- 40
known.pair <- structure(c(-46.9531139599816, -62.1874917150412,
25.9011462171242, 16.7441676243879), .Dim = c(2L, 2L),
.Dimnames = list(NULL, c("x", "y")))
## Distance and angle (/_KkB) between the two known points
d1 <- sqrt(sum(diff(known.pair)^2))
theta1 <- atan(do.call("/", as.list(rev(diff(known.pair)))))
## Calculate magnitude of /_KkC1 and /_KkC2
theta2 <- acos((d1/2)/r)
## Find center of one circle (using /_BkC1)
dx1 <- cos(theta1 + theta2)*r
dy1 <- sin(theta1 + theta2)*r
p1 <- known.pair[2,] + c(dx1, dy1)
## Find center of other circle (using /_BkC2)
dx2 <- cos(theta1 - theta2)*r
dy2 <- sin(theta1 - theta2)*r
p2 <- known.pair[2,] + c(dx2, dy2)
## Showing that it worked
library(sp)
library(rgeos)
plot(0,0, xlim = c(-200, 200), ylim = c(-200, 200), type = "n", asp = 1)
points(known.pair)
found.pt <- SpatialPoints(matrix(slvd$par, nrow = 1))
points(p1[1], p1[2], col="blue", pch=16)
points(p2[1], p2[2], col="green", pch=16)
This is the basic geometric way of going about solving it that everybody else is mentioning. I use polyroot to get the roots of the resulting quadratic equation but you could easily just use the quadratic equation directly.
# x is a vector containing the two x coordinates
# y is a vector containing the two y coordinates
# R is a scalar for the desired radius
findCenter <- function(x, y, R){
dy <- diff(y)
dx <- diff(x)
# The radius needs to be at least as large as half the distance
# between the two points of interest
minrad <- (1/2)*sqrt(dx^2 + dy^2)
if(R < minrad){
stop("Specified radius can't be achieved with this data")
}
# I used a parametric equation to create the line going through
# the mean of the two points that is perpendicular to the line
# connecting the two points
#
# f(k) = ((x1+x2)/2, (y1+y2)/2) + k*(y2-y1, x1-x2)
# That is the vector equation for our line. Then we can
# for any given value of k calculate the radius of the circle
# since we have the center and a value for a point on the
# edge of the circle. Squaring the radius, subtracting R^2,
# and equating to 0 gives us the value of t to get a circle
# with the desired radius. The following are the coefficients
# we get from doing that
A <- (dy^2 + dx^2)
B <- 0
C <- (1/4)*(dx^2 + dy^2) - R^2
# We could just solve the quadratic equation but eh... polyroot is good enough
k <- as.numeric(polyroot(c(C, B, A)))
# Now we just plug our solution in to get the centers
# of the circles that meet our specifications
mn <- c(mean(x), mean(y))
ans <- rbind(mn + k[1]*c(dy, -dx),
mn + k[2]*c(dy, -dx))
colnames(ans) = c("x", "y")
ans
}
findCenter(c(-2, 0), c(1, 1), 3)
Following @PhilH's solution, just using trigonometry in R:
radius=40
Draw the original points on the radius
plot(known.pair,xlim=100*c(-1,1),ylim=100*c(-1,1),asp=1,pch=c("a","b"),cex=0.8)
Find the midpoint c
of ab
(which is also the midpoint of de
the two circle centers)
AB.bisect=known.pair[2,,drop=F]/2+known.pair[1,,drop=F]/2
C=AB.bisect
points(AB.bisect,pch="c",cex=0.5)
Find the length and angle of the chord ab
AB.vector=known.pair[2,,drop=F]-known.pair[1,,drop=F]
AB.len=sqrt(sum(AB.vector^2))
AB.angle=atan2(AB.vector[2],AB.vector[1])
names(AB.angle)<-NULL
Calculate the length and angle of the line from c
to the centers of the two circles
CD.len=sqrt(diff(c(AB.len/2,radius)^2))
CD.angle=AB.angle-pi/2
Calculate and plot the position of the two centers d
and e
from the perpendicular to ab
and the length:
center1=C+CD.len*c(x=cos(CD.angle),y=sin(CD.angle))
center2=C-CD.len*c(x=cos(CD.angle),y=sin(CD.angle))
points(center1[1],center1[2],col="blue",cex=0.8,pch="d")
points(center2[1],center2[2],col="blue",cex=0.8,pch="e")
Shows:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With