# Parameter sampling from a 3D isosurface in R

Before we start, a small disclaimer: I am not a computer scientist, and the field of isosurfaces is new to me, so hopefully, the question is phrased clearly:) Otherwise, let me know, and I will (try to) improve it!

Ultimate Goal

I have a function that takes three parameters as arguments f(x,y,z) and outputs a single numerical value. The goal is to reverse this process: to sample, based on the input of a single value, potential combinations of parameters that correspond to that numerical value. For example, if f(1,1,1)=1 and f(2,1,1)=1, then I would like to input 1 and retrieve a list of [1,1,1], [2,1,1].

Approach

At first, I calculated the output of f(x,y,z) for a large grid of parameters. Since we have to deal with a 3-dimensional space, the Marching cubes algorithm seems appropriate to find all the parameter combinations that lead to similar function output. I am working in R and used the misc3d package that contains the computeContour3d function for calculating isosurfaces. For illustrative purposes, I have used the mean() function as f() in this example.

Let's first create a sample dataset:

library(tidyverse)
library(misc3d) # contains computeContour3d function

# Create a dataset of all combinations of parameters and apply function.
data <- expand_grid(x = seq(1, 51, 10), y = seq(0, -2, -0.5), z = seq(0, -3, -1)) %>%
rowwise() %>%
mutate(value = mean(c(x, y, z)))

data
#> # A tibble: 120 x 4
#>        x     y     z  value
#>  1     1   0       0  0.333
#>  2     1   0      -1  0
#>  3     1   0      -2 -0.333


This dataset needs to be turned into an array for use in the computeContour3d function:

# Turn dataframe into an 3d array
value <- data$$value size_x <- length(unique(data$$x))
size_y <- length(unique(data$$y)) size_z <- length(unique(data$$z))

data <- data[order( data$$x, data$$y, data$z),] dimensions <- c(size_x, size_y, size_z) v <- array(data = value, dim=dimensions) # Create subarrays that map indices back # to the original data vX <- array( data=data$$x, dim=dimensions ) vY <- array( data=data$$y, dim=dimensions ) vZ <- array( data=data$z, dim=dimensions )


Next, I wrote a function to calculate the isosurfaces corresponding to a certain level (i.e., a certain output of f(x,y,z)). The computeContour3d function outputs:

A matrix of three columns representing the triangles making up the contour surface. Each row represents a vertex, and groups of three rows represent a triangle.

isosurface <- function(level){
isosurface <-computeContour3d(vol = v,
maxvol = max(v),
level = level) %>%
as.data.frame() %>%
rowwise() %>%

# Map the indices of the output the original x,y,z values
mutate(x = vX[V1, V2, V3],
y = vY[V1, V2, V3],
z = vZ[V1, V2, V3]) %>%
ungroup()
return(isosurface)
}


Problem

Let's test the function, with 3 as level input:

isosurface(3) %>%
rowwise() %>%
mutate(value = mean(c(x, y, z))) %>%

#>      V1    V2    V3     x     y     z  value
#> 1   3    1.2   2       11  -0.5    -3  2.5
#> 2   3    1     1.95     1  -2      -1 -0.667
#> 3   2.8  1     2       11  -1       0  3.33
#> and more rows...

# The indices are translated correctly to the corresponding values
vX[3, 1.2, 2] #--> 11
vY[3, 1.2, 2] #--> -0.5
vZ[3, 1.2, 2] #--> -3


The indices are correctly translated to the corresponding values; however, if we put these x,y,z values into the function f(x,y,z) (=mean(c(x,y,z))), then the output value is 2.5 and not 3, as I would expect.

Question

Obviously, I am making a mistake somewhere (either conceptual or in the code itself...). So the questions are:

• What am I doing wrong?
• How do you sample parameter combinations from an isosurface level using R's misc3d package? Or is this not possible?
• Do you have a way to test your algorithm in 2D? That would make it more easy to simply plot the stages of your algorithm, and inspect where the problem lies. Maybe you could try to decouple your function by using f(x,y,z) = x+y or similar? – MPIchael Jan 8 at 13:21