[英]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:
基本的见解是:
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.