如何在rgl窗口中实现3D树状图与单个球体的独立交互式旋转?
实现3D极坐标树状图中单个球体的独立交互式旋转
背景
此前我在Stack Overflow上提问「如何在R的rgl窗口中绘制3D极坐标树状图」,user2554330的回答已解决基础绘制需求。现在我在树状图尖端添加了3D球体,但默认旋转场景时所有物体(树状图+所有球体)会一同转动,想要实现单个球体独立旋转的效果——比如旋转粉色球体时,树状图和其他三个球体保持静止。
现有代码
1. 构建树状图
a <- list() # 初始化空对象 # 定义合并规则: # 负数代表叶节点, # 正数代表合并后的聚类(对应$merge中的行号) a$merge <- matrix(c(-1, -2, -3, -4, 1, 2), nc=2, byrow=TRUE ) a$height <- c(1, 1.5, 3) # 定义合并高度 a$order <- 1:4 # 叶节点顺序(手动输入时可忽略) a$labels <- LETTERS[1:4] # 叶节点标签 class(a) <- "hclust" # 转为hclust对象 plot(a) # 绘制平面树状图 # 转为dendrogram对象 ad <- as.dendrogram(a) # 获取线段信息 library(ggdendro) dend_data <- dendro_data(ad, type = "rectangle")
2. 绘制3D极坐标树状图(来自user2554330的代码)
nodes <- dend_data$segments # 设置树末端间隙 gap <- 0 # 设置中心偏移量 offset <- 0 radius <- with(nodes, max(c(y, yend)) + offset) circ <- with(nodes, max(c(x, xend)) + gap) # 转换为极坐标 nodes$theta <- with(nodes, 2*pi*x/circ) nodes$thetaend <- with(nodes, 2*pi*xend/circ) nodes$r <- with(nodes, (radius - y)/radius) nodes$rend <- with(nodes, (radius - yend)/radius) # 提取水平和垂直线段 horiz <- subset(nodes, y == yend) vert <- subset(nodes, x == xend) library(rgl) open3d() #> glX #> 1 # 绘制垂直线段 x <- with(vert, as.numeric(rbind(r*cos(theta), rend*cos(theta)))) y <- with(vert, as.numeric(rbind(r*sin(theta), rend*sin(theta)))) segments3d(x, y, z = 0) # 绘制水平线段(转为圆弧,剔除半径为0的情况) horiz <- subset(horiz, r > 0) with(horiz, arc3d(from = cbind(r*cos(theta), r*sin(theta), 0), to = cbind(r*cos(thetaend), r*sin(thetaend), 0), center = c(0, 0, 0))) # 绘制标签 labels <- dend_data$labels labels$theta <- with(labels, 2*pi*x/circ) # 稍微偏移避免和线段重叠 labels$r <- with(labels, (radius - y)/radius + 0.1) with(labels, text3d(r*cos(theta), r*sin(theta), 0, label))
3. 添加尖端球体的代码
with(labels, spheres3d(r*cos(theta), r*sin(theta), 0, radius = 0.1, color = c("lightblue", "pink", "lightyellow", "lightgrey")))
解决方案:利用rgl子场景实现独立旋转
rgl默认将所有物体放在同一个主场景中,旋转时会同步变动。要实现单个物体独立旋转,需要使用**子场景(subscene)**功能,将需要单独控制的物体放在独立子场景中,主场景放置树状图和其他不需要单独旋转的物体。
修改后的代码示例
# 先在主场景绘制树状图、标签,以及除粉色球外的其他球体 open3d() # 绘制树状图线段和圆弧(复用之前的代码) segments3d(x, y, z = 0) with(horiz, arc3d(from = cbind(r*cos(theta), r*sin(theta), 0), to = cbind(r*cos(thetaend), r*sin(thetaend), 0), center = c(0, 0, 0))) # 绘制标签 with(labels, text3d(r*cos(theta), r*sin(theta), 0, label)) # 绘制非粉色球体 sphere_coords <- with(labels, cbind(r*cos(theta), r*sin(theta), 0)) # 索引1、3、4对应lightblue、lightyellow、lightgrey spheres3d(sphere_coords[c(1,3,4), ], radius = 0.1, color = c("lightblue", "lightyellow", "lightgrey")) # 为粉色球体创建独立子场景 pink_subscene <- subscene3d(parent = currentSubscene3d()) # 切换到子场景绘制粉色球体 useSubscene3d(pink_subscene) spheres3d(sphere_coords[2, ], radius = 0.1, color = "pink") # 切回主场景 useSubscene3d(currentSubscene3d()) # 设置子场景的交互控制:仅允许旋转子场景内的物体 # 禁用主场景对粉色子场景的联动旋转 par3d(subscene = pink_subscene, mouseMode = c("rotate", "zoom", "fov", "none")) # 主场景保持默认交互 par3d(subscene = currentSubscene3d(), mouseMode = c("rotate", "zoom", "fov", "none"))
操作说明
- 点击粉色球体所在区域时,旋转操作只会影响粉色球体;点击树状图或其他球体区域时,旋转操作会影响主场景中的所有物体(树+非粉色球)。
- 如果需要多个球体都能独立旋转,可为每个球体创建单独的子场景,重复上述子场景创建和绘制步骤即可。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

