xypolygon.S
#
# xypolygon.S
#
# $Revision: 1.7 $ $Date: 2005/04/30 01:37:15 $
#
# low-level functions defined for polygons in list(x,y) format
#
verify.xypolygon <- function(p, fatal=TRUE) {
whinge <- NULL
if(!is.list(p) || length(p) != 2 ||
length(names(p)) != 2 || any(sort(names(p)) != c("x","y")))
whinge <- "polygon must be a list with two components x and y"
else if(is.null(p$x) || is.null(p$y) || !is.numeric(p$x) || !is.numeric(p$y))
whinge <- "components x and y must be numeric vectors"
else if(length(p$x) != length(p$y))
whinge <- "lengths of x and y vectors unequal"
ok <- is.null(whinge)
if(!ok && fatal)
stop(whinge)
return(ok)
}
inside.xypolygon <- function(pts, polly, test01=TRUE, method="Fortran") {
# pts: list(x,y) points to be tested
# polly: list(x,y) vertices of a single polygon (n joins to 1)
# test01: logical - if TRUE, test whether all values in output are 0 or 1
verify.xypolygon(pts)
verify.xypolygon(polly)
x <- pts$x
y <- pts$y
xp <- polly$x
yp <- polly$y
npts <- length(x)
nedges <- length(xp) # sic
score <- rep(0, npts)
on.boundary <- rep(FALSE, npts)
switch(method,
Fortran={
#------------------ call Fortran routine ------------------
temp <- .Fortran(
"inxyp",
x=as.double(x),
y=as.double(y),
xp=as.double(xp),
yp=as.double(yp),
npts=as.integer(npts),
nedges=as.integer(nedges),
score=as.double(score),
onbndry=as.logical(on.boundary),
PACKAGE="spatstat"
)
score <- temp$score
on.boundary <- temp$onbndry
},
interpreted={
#----------------- original interpreted code --------------
for(i in 1:nedges) {
x0 <- xp[i]
y0 <- yp[i]
x1 <- if(i == nedges) xp[1] else xp[i+1]
y1 <- if(i == nedges) yp[1] else yp[i+1]
dx <- x1 - x0
dy <- y1 - y0
if(dx < 0) {
# upper edge
xcriterion <- (x - x0) * (x - x1)
consider <- (xcriterion <= 0)
if(any(consider)) {
ycriterion <- y[consider] * dx - x[consider] * dy
+ (x0 * dy - y0 * dx)
# closed inequality
contrib <- (ycriterion >= 0) *
ifelse(xcriterion[consider] == 0, 1/2, 1)
# positive edge sign
score[consider] <- score[consider] + contrib
# detect whether any point lies on this segment
on.boundary[consider] <-
on.boundary[consider] | (ycriterion == 0)
}
} else if(dx > 0) {
# lower edge
xcriterion <- (x - x0) * (x - x1)
consider <- (xcriterion <= 0)
if(any(consider)) {
ycriterion <- y[consider] * dx - x[consider] * dy
+ (x0 * dy - y0 * dx)
# open inequality
contrib <- (ycriterion < 0) *
ifelse(xcriterion[consider] == 0, 1/2, 1)
# negative edge sign
score[consider] <- score[consider] - contrib
# detect whether any point lies on this segment
on.boundary[consider] <-
on.boundary[consider] | (ycriterion == 0)
}
} else {
# vertical edge
consider <- (x == x0)
if(any(consider)) {
# zero score
# detect whether any point lies on this segment
yconsider <- y[consider]
ycriterion <- (yconsider - y0) * (yconsider - y1)
on.boundary[consider] <-
on.boundary[consider] | (ycriterion <= 0)
}
}
}
},
stop("Unrecognised choice for \"method\"")
)
#------------------- END SWITCH ------------------------------
# any point recognised as lying on the boundary gets score 1.
score[on.boundary] <- 1
if(test01) {
# check sanity
if(!all((score == 0) | (score == 1)))
warning("internal error: some scores are not equal to 0 or 1")
}
attr(score, "on.boundary") <- on.boundary
return(score)
}
area.xypolygon <- function(polly) {
#
# polly: list(x,y) vertices of a single polygon (n joins to 1)
#
verify.xypolygon(polly)
xp <- polly$x
yp <- polly$y
nedges <- length(xp) # sic
# place x axis below polygon
yp <- yp - min(yp)
# join vertex n to vertex 1
nxt <- c(2:nedges, 1)
# x step, WITH sign
dx <- xp[nxt] - xp
# average height
ym <- (yp + yp[nxt])/2
-sum(dx * ym)
}
bdrylength.xypolygon <- function(polly) {
verify.xypolygon(polly)
xp <- polly$x
yp <- polly$y
nedges <- length(xp)
nxt <- c(2:nedges, 1)
dx <- xp[nxt] - xp
dy <- yp[nxt] - yp
sum(sqrt(dx^2 + dy^2))
}
reverse.xypolygon <- function(p) {
# reverse the order of vertices
# (=> change sign of polygon)
verify.xypolygon(p)
return(lapply(p, rev))
}
overlap.xypolygon <- function(P, Q) {
# compute area of overlap of two simple closed polygons
verify.xypolygon(P)
verify.xypolygon(Q)
xp <- P$x
yp <- P$y
np <- length(xp)
nextp <- c(2:np, 1)
xq <- Q$x
yq <- Q$y
nq <- length(xq)
nextq <- c(2:nq, 1)
# adjust y coordinates so all are nonnegative
ylow <- min(c(yp,yq))
yp <- yp - ylow
yq <- yq - ylow
area <- 0
for(i in 1:np) {
ii <- c(i, nextp[i])
xpii <- xp[ii]
ypii <- yp[ii]
for(j in 1:nq) {
jj <- c(j, nextq[j])
area <- area +
overlap.trapezium(xpii, ypii, xq[jj], yq[jj])
}
}
return(area)
}
overlap.trapezium <- function(xa, ya, xb, yb, verb=FALSE) {
# compute area of overlap of two trapezia
# which have same baseline y = 0
#
# first trapezium has vertices
# (xa[1], 0), (xa[1], ya[1]), (xa[2], ya[2]), (xa[2], 0).
# Similarly for second trapezium
# Test for vertical edges
dxa <- diff(xa)
dxb <- diff(xb)
if(dxa == 0 || dxb == 0)
return(0)
# Order x coordinates, x0 < x1
if(dxa > 0) {
signa <- 1
lefta <- 1
righta <- 2
if(verb) cat("A is positive\n")
} else {
signa <- -1
lefta <- 2
righta <- 1
if(verb) cat("A is negative\n")
}
if(dxb > 0) {
signb <- 1
leftb <- 1
rightb <- 2
if(verb) cat("B is positive\n")
} else {
signb <- -1
leftb <- 2
rightb <- 1
if(verb) cat("B is negative\n")
}
signfactor <- signa * signb # actually (-signa) * (-signb)
if(verb) cat(paste("sign factor =", signfactor, "\n"))
# Intersect x ranges
x0 <- max(xa[lefta], xb[leftb])
x1 <- min(xa[righta], xb[rightb])
if(x0 >= x1)
return(0)
if(verb) {
cat(paste("Intersection of x ranges: [", x0, ",", x1, "]\n"))
abline(v=x0, lty=3)
abline(v=x1, lty=3)
}
# Compute associated y coordinates
slopea <- diff(ya)/diff(xa)
y0a <- ya[lefta] + slopea * (x0-xa[lefta])
y1a <- ya[lefta] + slopea * (x1-xa[lefta])
slopeb <- diff(yb)/diff(xb)
y0b <- yb[leftb] + slopeb * (x0-xb[leftb])
y1b <- yb[leftb] + slopeb * (x1-xb[leftb])
# Determine whether upper edges intersect
# if not, intersection is a single trapezium
# if so, intersection is a union of two trapezia
yd0 <- y0b - y0a
yd1 <- y1b - y1a
if(yd0 * yd1 >= 0) {
# edges do not intersect
areaT <- (x1 - x0) * (min(y1a,y1b) + min(y0a,y0b))/2
if(verb) cat(paste("Edges do not intersect\n"))
} else {
# edges do intersect
# find intersection
xint <- x0 + (x1-x0) * abs(yd0/(yd1 - yd0))
yint <- y0a + slopea * (xint - x0)
if(verb) {
cat(paste("Edges intersect at (", xint, ",", yint, ")\n"))
points(xint, yint, cex=2, pch="O")
}
# evaluate left trapezium
left <- (xint - x0) * (min(y0a, y0b) + yint)/2
# evaluate right trapezium
right <- (x1 - xint) * (min(y1a, y1b) + yint)/2
areaT <- left + right
if(verb)
cat(paste("Left area = ", left, ", right=", right, "\n"))
}
# return area of intersection multiplied by signs
return(signfactor * areaT)
}