You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 09:09:55