使用R的长方体中的部分球面积

时间:2014-07-03 16:29:55

标签: r geometry area

我有一个球体,其中心和半径的x,y和z坐标给出。此外,还有一个长方体,其中给出了六个平面的等式。我们只知道这个球体的中心位于长方体内部。半径可以使得球体可以完全/部分地位于长方体内(从长方体的任何位置突出)。 [注意:球体永远不会完全在长方体之外]

我想找到长方体内球体部分的表面积与球体总面积之比。

我可以对http://mathworld.wolfram.com/SphericalCap.html给出的公式进行编码,但它不适用于所有情况(例如,当球体从长方体的2个/更多边缘突出或从长方体的顶点突出时)

有人可以帮帮我吗?谢谢。

Edit1: 例如:如果球体在立方体内部的一半而在外面的一半,那么ans将是1/2。

1 个答案:

答案 0 :(得分:1)

除了公式方法之外,您还可以使用数组进行任意精细的求和,以获得足够接近的近似值。您将必须微调网格并完成两个功能:如何确定网格上的点是否在长方体内,以及如何近似网格中将存在于整个球体中的点数。两者都不应该太难。

 sphere <- list(x = 0.5, y = 0.4, z = 0.1, radius = 0.8)
 # The equations of the six planes
 cuboid <- as.character(outer(c("x","y","z"), c(0,1), paste, sep = ' = '))
 # "x = 0" "y = 0" "z = 0" "x = 1" "y = 1" "z = 1"

 in_sphere <- function(x, y, z)
   (x - sphere$x)^2 + (y - sphere$y)^2 + (z - sphere$z)^2 <= sphere$radius^2
 in_cuboid <- function(x, y, z) {
   # TODO: This seems tricky, fill it in.
   # It's easy to do for a box, but you may have to do some linear algebra to
   # to do it for a cuboid. For example, an easy computational way to tell if
   # a point lies "inside" a cuboid is to iterate over each side, and take
   # the  line between the side and the point that gives the closest distance of the
   # point to the side. Then take the intersection of this line with the remaining
   # sides and you should get only one other assuming the cuboid is convex. Then,
   # if the distance between the two points on the two sides is less than the distances
   # between the tested point and either side, the point lies outside the cuboid.
   # Otherwise, it is inside the cuboid.
 }

 # You need to pre-determine bounds on your grid to search.
 # This may be an interesting sub-problem. Note it is hard in general because
 # two sides of a cuboid can intersect arbitrarily far in 3-space with
 # arbitrarily small modifications of their coefficients.
 dims <- c(10, 10, 10) # The number of points in each dimensions to check
 box <- c(2, 2, 2)     # The actual x-y-z dimensions of the box.
 grid <- array(dim = dims, logical(Reduce(`*`, dims)))

 iterate <- function(expr, over_points = TRUE) { # Iterate an expression over the grid.
   force(over_points)
   eval.parent(substitute({
     .seqs <- lapply(seq_along(dims), function(i) seq(0, to = box[i], length.out = dims[i]))
     for(.x in seq_len(dims[1]))
       for(.y in seq_len(dims[2]))
         for(.z in seq_len(dims[3])) {
           if (over_points) { x <- .seqs[[1]][.x]; y <- .seqs[[2]][.y]; z <- .seqs[[3]][.z] }
           else { x <- .x; y <- .y; z <- .z }
           expr
         }
   }))
 }
 # Mark which points lie in the intersection
 iterate(grid[.x, .y, .z] <- in_sphere(x, y, z) && in_cuboid(x, y, z))

 # Count the points lying on each surface component.
 cuboid_points <- 0; sphere_points <- 0
 threshold <- 2*box / min(dims) # If the distance from a point is less than this
                              # we count it as lying "on" a surface component.
                              # This is not perfect but should work for a large
                              # enough grid -- basic calculus.

 # Replace the cuboid side equations with the epsilon equations.
 cuboid_epsilons <- sapply(cuboid, gsub, pattern = "^(.*)=(.*)$", replacement = paste0("abs(\\1 - \\2) <= ", threshold))
 # > cuboid_epsilons
 #                x = 0                y = 0                z = 0                x = 1                y = 1                z = 1
 # "abs(x  -  0) <= 0.1" "abs(y  -  0) <= 0.1" "abs(z  -  0) <= 0.1" "abs(x  -  1) <= 0.1" "abs(y  -  1) <= 0.1" "abs(z  -  1) <= 0.1"

 # Whether or not a point lies close to any of the sides.
 cuboid_epsilon_condition <- parse(text = paste(cuboid_epsilons, collapse = ' || '))
 #  expression(abs(x  -  0) <= 0.1 || abs(y  -  0) <= 0.1 || abs(z  -  0) <= 0.1 || abs(x  -  1) <= 0.1 || abs(y  -  1) <= 0.1 || abs(z  -  1) <= 0.1)

 # Whether or not a point lies close to the surface of the sphere.
 sphere_epsilon_condition <- parse(text =
   paste0('abs(', paste(sapply(c('x','y','z'), function(i) paste0('(', i, ' - ', sphere[[i]], ')^2')), collapse = ' + '),
          ' - ', sphere$radius, '^2) < ', threshold))
 # expression(abs((x - 0.5)^2 + (y - 0.4)^2 + (z - 0.1)^2 - 0.8) < 0.1^2)

 iterate({
   if (grid[.x, .y, .z]) { # In the intersection of cuboid and sphere
     if (eval(cuboid_epsilon_condition)) # Lies close to any of the sides
       cuboid_points <- cuboid_points + 1
     else if (eval(sphere_epsilon_condition)) # Lies close to sphere's surface
       sphere_points <- sphere_points + 1
   }
 })

 number_of_points_in_a_grid_of_that_size_on_the_total_surface_of_the_sphere <-
   # TODO: Fill this in. Shouldn't be too hard.

 cat("The ratio of the intersection of the surface of the sphere with the cuboid to the",
     "total area of the sphere is approximately: ",
     sphere_points / number_of_points_in_a_grid_of_that_size_on_the_total_surface_of_the_sphere)