简体   繁体   English

如何将 plot 个向量正交到四边形网格?

[英]How to plot vectors orthogonal to quadmesh?

I am trying to use the rgl package in R to plot unit vectors orthogonal to each cell in a quadmesh.我正在尝试在 R 到 plot 单位向量中使用rgl package 与四边形网格中的每个单元格正交。 I've used this page for a bit of guidance, and currently have plotted a quadmesh surface and some placeholder vectors using arrow3d , but I'd like to get these vectors to be orthogonal to the cell they're contained within and to all be the same length (eg length of 1).我已经使用此页面进行了一些指导,目前已经使用arrow3d绘制了一个四边形网格表面和一些占位符向量,但我想让这些向量与它们所包含的单元格正交并且全部相同的长度(例如长度为 1)。 Does anyone know how to get the proper endpoint to put into arrow3d to satisfy this condition, or have a different approach?有谁知道如何将正确的端点放入arrow3d以满足此条件,或者有不同的方法?

library(raster)
library(rgl)
library(quadmesh)

m <- matrix(c(seq(0, 0.5, length = 5), 
              seq(0.375, 0, length = 4)), 3)
x <- seq(1, nrow(m)) - 0.5
y <- seq(1, ncol(m)) - 0.5
r <- raster(list(x = x, y = y, z = m))

qm <- quadmesh(r)
image(r)
op <- par(xpd = NA)
text(t(qm$vb), lab = 1:ncol(qm$vb)) #Plot index numbers for vertices

在此处输入图像描述

vrts<- list(c(9,10,14,13),
            c(10,11,15,14),
            c(11,12,16,15),
            c(5,6,10,9),
            c(6,7,11,10),
            c(7,8,12,11),
            c(1,2,6,5),
            c(2,3,7,6),
            c(3,4,8,7)) #Index for vertices of each raster cell starting from bottom left and moving to the right

shade3d(qm, col = "firebrick")
axes3d()
title3d(xlab="X", ylab="Y", zlab="Z")
for (i in 1:9) {
  row_number<- floor((i-1)/3)+1
  col_number<- ((i-1)%%3)+1
  p<- apply(qm$vb[1:3,  vrts[[i]]], mean, MARGIN = 1) #get current xyz position of raster cell
  rgl.spheres(x = p[1], y = p[2], z = p[3], r=0.1) #Plot points
  p0<- c(x[col_number], y[row_number],p[3]) #arrow start point
  p1<- c(x[col_number],y[row_number],p[3]+1) #arrow end point
  arrow3d(p0, p1) #Plot arrow
}

在此处输入图像描述

This is tricky.这很棘手。 The fundamental insights are that:基本的见解是:

  1. Each square in the mesh can be thought of as a plane网格中的每个正方形都可以被认为是一个平面
  2. Each plane can be defined by a formula z = a x + b y + c每个平面都可以用公式 z = a x + b y + c来定义
  3. The coefficients a , b and c can be found by running a linear regression on the 4 vertices than lie at the corners of each square.系数abc可以通过对位于每个正方形角上的 4 个顶点运行线性回归来找到。
  4. The vector [ -a , -b , 1 ] is normal to the plane矢量 [ -a , -b , 1 ] 垂直于平面
  5. The vector [ -a , -b , 1 ] / sqrt( + + ) has length 1向量 [ -a , -b , 1 ] / sqrt( + + ) 的长度为 1
  6. The arrow bases are at the midpoint of each square箭头底部位于每个正方形的中点
  7. The arrow tips are at the bases of each square plus the vector [ -a , -b , 1 ] / sqrt( + + )箭头提示位于每个正方形的底部加上向量 [ -a , -b , 1 ] / sqrt( + + )

To implement this, we first define a couple of helper functions:为了实现这一点,我们首先定义了几个辅助函数:

midpoints <- function(x) diff(x)/2 + x[-length(x)]

centers_from_vertices <- function(v) {
  apply(apply(v, 1, midpoints), 1, midpoints)
}

Now we can create lists of x, y, z points for the vertices and the center points like this:现在我们可以为顶点和中心点创建 x、y、z 点列表,如下所示:

vertices <- lapply(asplit(qm$vb, 1), `dim<-`, value = dim(m) + 1)
centres  <- lapply(vertices, centers_from_vertices)

Rather than having to count all the vertices and create a list belonging to each square, we can automate the process and have the indices of the vertices for each square in a list.不必计算所有顶点并创建属于每个正方形的列表,我们可以自动化该过程并在列表中为每个正方形提供顶点索引。

index <- matrix(seq(prod(dim(m) + 1)), nrow = nrow(m) + 1, byrow = TRUE)

indices <- unlist(lapply(seq(dim(m)[1]), function(i) {
  lapply(seq(dim(m)[2]), function(j) {
    index[0:1 + i, 0:1 + j]
    })
  }), recursive = FALSE)

Now we can get the end-points of each arrow using the insights above:现在我们可以使用上面的见解获得每个箭头的终点:

ends <- lapply(asplit(sapply(indices, function(i) {
  co <- coef(lm(z~x+y,  as.data.frame(lapply(vertices, c))[c(i),]))
  co <- -c(co[2], co[3], z = -1)
  co/sqrt(sum(co^2))
  }), 1), c)

ends <- Map(`+`, centres[1:3], ends)

Finally, we can draw the result:最后,我们可以得出结果:

shade3d(qm, col = "firebrick")
axes3d()
title3d(xlab="X", ylab="Y", zlab="Z")
rgl.spheres(x = centres$x, y = centres$y, z = centres$z, r = 0.1)

for(i in 1:9) {
  arrow3d(c(centres$x[i], centres$y[i], centres$z[i]),
          c(ends$x[i], ends$y[i], ends$z[i]))
}

在此处输入图像描述

The rgl:::showNormals() function does this. rgl:::showNormals() function 执行此操作。 It's an internal function, used for debugging, and it plots the normals that are included in the mesh object, at each vertex.它是一个内部 function,用于调试,它在每个顶点绘制包含在网格 object 中的法线。

There are a couple of issues for using this function with your data.将此 function 与您的数据一起使用存在几个问题。 First, being an internal function, it's subject to change without notice.首先,作为内部 function,它如有更改,恕不另行通知。 So I'd make a copy of it and work with that.所以我会复制它并使用它。

Second, it plots the normals component of the mesh, and your mesh doesn't have one.其次,它绘制网格的normals分量,而您的网格没有。 You can use the rgl::addNormals() function to add normals, and then it will work.您可以使用rgl::addNormals() function 添加法线,然后它就会起作用。 For example,例如,

qmn <- addNormals(qm)
rgl:::showNormals(qmn)

截屏

If you want the normals to point up, you can say qmn$normals <- -qmn$normals before plotting.如果你想让法线向上,你可以在绘图前说qmn$normals <- -qmn$normals

The third issue is that it uses the ugly "lines" type of arrow.第三个问题是它使用了丑陋的"lines"类型的箭头。 But if you've got a copy of the function, you could modify that.但是,如果您有 function 的副本,则可以对其进行修改。

The fourth issue is that the arrows are at the vertices, not the centers.第四个问题是箭头在顶点,而不是中心。 If you really want them at the centers, you'll need to calculate normals there.如果你真的想要它们在中心,你需要在那里计算法线。 Normals to a pair of vectors are calculated using the cross product (eg rgl:::xprod() ), but you'll have to decide which pair of vectors to use.使用叉积(例如rgl:::xprod() )计算一对向量的法线,但您必须决定使用哪对向量。 rgl doesn't force quads to be planar; rgl不强制四边形是平面的; I don't know if quadmesh does.我不知道quadmesh是否可以。 This means that you might have more than one choice for the normal to the quad.这意味着对于四边形的法线,您可能有不止一种选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM