Area: Group Project (Group 18)

Convex Hull

In the section Monte Carlo (Part 1), we mention convex hull of the concave polygon as the circumscripe convex polygon of it. Actually, given a set of points, its convex hull[18] is the convex polygon constructed from the selected points that contains all the points. It is possible to find the convex hull in easy way, in Monte Carlo (Part 1) the function outbound_convex can use to find the convex hull of a less complicate simple polygon, i.e. winding number of a point inside is less than 2 generally.

However, if given an arbitrary set of points, that function will not work.

There is a function in R called chull. Just like its name, given an arbitrary set of points, it will return the index of the points that form the convex hull.

For example:

k3 <- 1000
xr2 <- runif(k3,-10,10) 
yr2 <- runif(k3,-10,10)
inx <- chull(xr2,yr2)
plot(xr2,yr2,pch=".")
polygon(xr2[inx],yr2[inx])
graphy
Figure 8.1: Convex hull of an arbitrary set of points
area_mc_method_v4(xr2[inx],yr2[inx],plotpoly = FALSE,typepoly = FALSE)
## [1] 390.754

Also it's worth to notice that, if we generate the points over a rectangle uniformly, then the area of convex hull will approximately the same as the area of the rectangle. As we uniformly generate those points, there must be points around four corners, so the convex hull just looks like the rectangle itself. So we can say, if we generate $n$ points, as $n$ tends to infinity, the convex hull will tends to the region used to generate the points.

Apart from finding the convex hull, we can use the type of polygon function to test a large set of points. For example, if the first 5 points form a convex polygon, but the six points will make it self-intersecting or becomes concave, then we skip the point and move on to the next one. Code would look like this:

k2 <- 1000 # Number of points
bad_point_x <- c()
bad_point_y <- c()
xr1 <- runif(k2,-10,10) 
yr1 <- runif(k2,-10,10)
for (i in 3:k2) {
  if (length(bad_point_x) == 0) {
    if (type_of_polygon(xr1[1:i], yr1[1:i]) != "Convex") {
      bad_point_x <- c(bad_point_x, i)
      bad_point_y <- c(bad_point_y, i)
    }
  } else {
    if (type_of_polygon(xr1[1:i][-bad_point_x], yr1[1:i][-bad_point_y]) != "Convex") {
      bad_point_x <- c(bad_point_x, i)
      bad_point_y <- c(bad_point_y, i)
    }
  }
  if (i == k2) {
    plot(xr1,yr1, pch = ".")
    polygon(xr1[1:i][-bad_point_x],yr1[1:i][-bad_point_y])
  }
}
graphy
Figure 8.2: Convex polygon from an arbitrary set of points

However, the limitation is very obvious. This method normally fails with a lot of "bad points" at the end.

Also, we can reorder the points: Instead of the given order, the furthest point from the centre of the set of points will be the first one, and the nearest one will be the last one. Then the method above will likely return a larger polygon.

k2 <- 1000 # Number of points
bad_point_x <- c()
bad_point_y <- c()
xr1 <- runif(k2,-10,10) 
yr1 <- runif(k2,-10,10)
mean_xr1 <- mean(xr1)
mean_yr1 <- mean(yr1)
ordered_xr1 <- c()
ordered_yr1 <- c()
for (i in 1:k2) {
  ordered_xr1[i] <- xr1[sort((xr1 - mean_yr1)^2 + (yr1 - mean_yr1)^2, decreasing = TRUE, index.return = TRUE)$ix[i]]
  ordered_yr1[i] <- yr1[sort((xr1 - mean_yr1)^2 + (yr1 - mean_yr1)^2, decreasing = TRUE, index.return = TRUE)$ix[i]]
}
for (i in 3:k2) {
  if (length(bad_point_x) == 0) {
    if (type_of_polygon(ordered_xr1[1:i], ordered_yr1[1:i]) != "Convex") {
      bad_point_x <- c(bad_point_x, i)
      bad_point_y <- c(bad_point_y, i)
    }
  } else {
    if (type_of_polygon(ordered_xr1[1:i][-bad_point_x], ordered_yr1[1:i][-bad_point_y]) != "Convex") {
      bad_point_x <- c(bad_point_x, i)
      bad_point_y <- c(bad_point_y, i)
    }
  }
  if (i == k2) {
    plot(ordered_xr1,ordered_yr1,pch=".")
    polygon(ordered_xr1[1:i][-bad_point_x],ordered_yr1[1:i][-bad_point_y])
  }
}
graphy
Figure 8.3: Large convex polygon from an arbitrary set of points

But like the first one, this method normally fails with a lot of "bad points" at the end. Figure 8.3 is just an ideal example from the code above. It doesn't mean we can't do something, like set a while loop inside, detect the area. If the area is less than, like 90% of the rectangle, then repeat until the area is larger than that.

Generate Random Polygon

It is also possible to use the type of polygon function to generate a random simple polygon. Just put it into a while loop, and keep generating points inside a region. If the point generated form a convex/concave polygon in the given order, then stop the while, else repeat the process.

generate_poly <- function(n, convex = TRUE) { # Must faster if convex is TRUE
  simple <- FALSE
  while (!simple) {
    x <- runif(n,-2,2) # If convex n < 9, if !convex n < 7
    y <- runif(n,-2,2)
    if (!convex) type <- type_of_polygon_v2(x,y) else if (convex) type <- type_of_polygon(x,y)
    if (!convex) {
      if (type == "Convex" || type == "Concave") {
        plot(x,y,asp = 1,pch = "")
        polygon(x,y)
        simple <- TRUE
      }
    } else if (convex) {
      if (type == "Convex") {
        plot(x,y,asp = 1,pch = "")
        polygon(x,y)
        simple <- TRUE
      }
    }
  }
  return(list(x = x, y = y))
}

Like the function above, it generates a $n$-side polygon randomly. But when $n$ gets larger, it will be extremely slow, so $n \leq 8$ is recommended.

generate_poly(8) # n = 6 with convex = FALSE is terrible already
graphy
Figure 8.4: Random convex polygon
## $x
## [1]  1.5902308 -0.6844604 -1.2514097 -1.7701839 -1.2096977 -0.8694716  0.4245622  1.2072511
## 
## $y
## [1]  0.94380260  0.87501177  0.59655934 -0.04909636 -1.26849916 -1.64980162 -1.05089891 -0.67585441

We can also generate a random polygon by setting a centre point first, and then span the angle and distance from the point randomly, also limit the sum of angle to $2\pi$. This method is much faster and it works very well, we can also use it to generate lattice polygon and calculate the area by Pick's theorem.

generate_poly_v2 <- function(n, area=FALSE, int = FALSE) { # If int, pls not make n too large
  r <- c()
  angle <- c()
  x <- c()
  y <- c()
  for (i in 1:n) {
    r[i] <- runif(1,0.5,1.5)
    angle[i] <- runif(1,0.9*2*pi/n,1.1*2*pi/n)
  }
  cumangle <- cumsum(angle)
  if (int) {
    for (i in 1:n) {
      x[i] <- round(4*r[i]*cos(cumangle[i]))
      y[i] <- round(4*r[i]*sin(cumangle[i]))
    }
    grad <- gradient(x,y)
    inx <- c(n+1)
    nan_grad <- FALSE
    for (i in 1:n) {
      if (is.nan(grad[i])) {
        inx <- c(inx,i)
        nan_grad <- TRUE
      }
    }
    x <- x[-inx];y <- y[-inx]
    x2 <- start_from_minx_v2(x,y)$x
    y2 <- start_from_minx_v2(x,y)$y
  } else for (i in 1:n) {
    x[i] <- r[i]*cos(cumangle[i])
    y[i] <- r[i]*sin(cumangle[i])
  }
  if(area) {
    if (int) return(picks_theorem(x2,y2)) else return(area_mc_method_v4(x,y))
  } else plot(x,y,pch="");polygon(x,y)
}

Example 8.1  Generate a random simple lattice polygon and find the area by Pick's Theorem.

generate_poly_v2(20,area=TRUE,int=TRUE) # Too large n might cause self-intersecting polygon
graphy
Figure 8.5: Random lattice polygon
## [1] 41

Or else, we can use Monte Carlo method to find the area of the random polygon.

Example 8.2  Generate a random simple polygon and find the area by Monte Carlo method.

generate_poly_v2(20,area = TRUE)
graphy
Figure 8.6: Random Concave polygon
## [1] 3.732384

Bibliography

© Group 18