R/slice2d.R

Defines functions common.cross.range simplex.point.intersection intersect.simplices

EPS = sqrt(.Machine$double.eps)
#EPS = 1e-9

empty.slice = data.frame(p1_1=c(), p1_2=c(), p2_1=c(), p2_2=c())

intersect.simplices = function(mesh, fp, d1, d2, use.3d.intersection=FALSE) {
  n = nrow(mesh$simplices)
  pts = matrix(unlist(mesh$points), nrow=nrow(mesh$points))
  simpls = matrix(unlist(mesh$simplices), nrow=nrow(mesh$simplices))
  fp.vect = unlist(fp)
  if(use.3d.intersection) {
    purrr::map_dfr(1:n,
           function(i) intersect.tri(mesh$points[mesh$simplices[i,],], fp, d1, d2))
  } else {
    purrr::map_dfr(1:n,
           function(i) simplex.point.intersection(pts[simpls[i,],], fp.vect, d1, d2))
  }
}

simplex.point.intersection = function(simplex, focus.pt, d1, d2) {
  # TODO: do some argument checking

  n = length(focus.pt)+1 # number of lambdas to check, dimensionality of the space+1

  # This does T = rbind(t(simplex), rep(1,nrow(simplex))) but is sometimes faster
  T = matrix(1, ncol=n, nrow=n) # T needs to be square
  T[1:n-1,1:n-1] = t(simplex)
  T[,n] = c(focus.pt, 1)
  det.T = det(T)
  if(det.T == 0) { # extra focus point may be in the simplex
    T[-1,n] = focus.pt + EPS # add small amount to make matrix non-singular
    det.T = det(T)
  }

  # if T is still singluar then the simplex lies in the plane
  if(det.T==0) {
    rows = t(combn(nrow(simplex), 2))
    res = data.frame(cbind(simplex[rows[,1], c(d1,d2)], simplex[rows[,2], c(d1,d2)]))
    names(res) = c("p1_1", "p1_2", "p2_1", "p2_2")
    return(res)
  }

  # compute lambda as best we can (there will be 3 parts)
  rr = c(focus.pt, 1)
  rr[c(d1,d2)] = 0
  rr.x = array(0,length(rr))
  rr.y = array(0,length(rr))
  rr.x[d1] = rr.y[d2] = 1
  # we are trying to compute T^(-1) . [x-xn,y-yn,...,z-zn]
  lambda.c = solve(T, rr) # column vector multiplication
  lambda.x = solve(T, rr.x)
  lambda.y = solve(T, rr.y)

  # find the d1 and d2 ranges that make each lambda 0
  # most indices are based on solving ax + by + c = 0
  # but keeping the other lambdas between 0 and 1

  # put y=mx+b into each other lambda formula and try and get a good range
  ranges = common.cross.range(lambda.x, lambda.y, lambda.c, n) # only consider the last lambda
  if(!is.na(ranges$x[1])) {
    list(p1_1=ranges$x[1], p2_1=ranges$x[2], p1_2=ranges$y[1], p2_2=ranges$y[2])
  } else {
    empty.slice
  }
}

common.cross.range = function(lambda.x, lambda.y, lambda.c, i) {
  x.rng = c(NA, NA)
  y.rng = c(NA, NA)
  # if one of the factors is 0 things need to be done differently
  if(lambda.x[i] == 0 & lambda.y[i] == 0) { # FIXME: this could be done more elegantly
    # no intersection
    x.rng = c(NA, NA)
    y.rng = c(NA, NA)
  } else if(lambda.x[i] == 0) {
    # solve ly * y + c = 0
    y = -lambda.c[i] / lambda.y[i]
    # replace y in all other formulas and solve lx * x + ly * y + lc >=0 for x
    xs = lambda.x[-i]
    cs = lambda.y[-i] * y + lambda.c[-i]
    res = -cs / xs
    if(any(xs==0) & any(cs[xs==0] < 0)) { # no intersection
      x.rng = c(NA, NA)
      y.rng = c(NA, NA)
    } else {
      xs.p = xs[is.finite(res)] # for filtering res later
      res = res[is.finite(res)]
      x.rng = c(max(res[xs.p>=0]), min(res[xs.p<0]))
      y.rng = c(y, y)
      # some bounds checking
      if(x.rng[2]-x.rng[1]<(-EPS)) { # y may be in the wrong direction
        x.rng = c(NA, NA)
        y.rng = c(NA, NA)
      }
    }
  } else if(lambda.y[i] == 0) {
    # solve lx * x + c = 0
    x = -lambda.c[i] / lambda.x[i]
    # replace x in all other formulas and solve lx * x + ly * y + lc >=0 for y
    ys = lambda.y[-i]
    cs = lambda.x[-i] * x + lambda.c[-i]
    res = -cs / ys
    if(any(ys==0) & any(cs[ys==0] < 0)) { # no intersection
      x.rng = c(NA, NA)
      y.rng = c(NA, NA)
    } else {
      res = -cs / ys
      ys.p = ys[is.finite(res)] # for filtering res later
      res = res[is.finite(res)]
      x.rng = c(x, x)
      y.rng = c(max(res[ys.p>=0]), min(res[ys.p<0]))
      # some bounds checking
      if(y.rng[2]-y.rng[1]<(-EPS)) { # y may be in the wrong direction
        x.rng = c(NA, NA)
        y.rng = c(NA, NA)
      }
    }
  } else {
    # assumes lx[i] and ly[i] are non-zero
    # solve lx * x + ly * y + c = 0 and substitute
    xs = lambda.x[-i] - lambda.y[-i] * lambda.x[i] / lambda.y[i]
    cs = lambda.c[-i] - lambda.y[-i] * lambda.c[i] / lambda.y[i]
    # need to solve xs + cs >= 0
    res = -cs / xs
    if(any(xs==0) & any(cs[xs==0] < 0)) { # no intersection
      x.rng = c(NA, NA)
      y.rng = c(NA, NA)
    } else {
      xs.p = xs[is.finite(res)] # for filtering res later
      res = res[is.finite(res)]
      x.rng = c(max(res[xs.p>=0]), min(res[xs.p<0])) # divide by negative switches the inequality
      y.rng = (-lambda.c[i] -lambda.x[i] * x.rng) / lambda.y[i]
      # some bounds checking
      if(x.rng[2]-x.rng[1]<(-EPS)) { # y may be in the wrong direction
        x.rng = c(NA, NA)
        y.rng = c(NA, NA)
      }
    }
  }

  list(x=x.rng, y=y.rng)
}
gabysbrain/hypersliceplorer documentation built on Nov. 17, 2022, 1:42 p.m.