I want to create a polar dendrogram in 3D in rgl window in R.
I adpated the code here, which was orginally intended for creating a 2D dendrogram (not polar), to create the dendrogram in 3D rgl window:
a <- list() # initialize empty object
# define merging pattern:
# negative numbers are leaves,
# positive are merged clusters (defined by row number in $merge)
a$merge <- matrix(c(-1, -2,
-3, -4,
1, 2), nc=2, byrow=TRUE )
a$height <- c(1, 1.5, 3) # define merge heights
a$order <- 1:4 # order of leaves(trivial if hand-entered)
a$labels <- LETTERS[1:4] # labels of leaves
class(a) <- "hclust" # make it an hclust object
plot(a) # look at the result
# Convert to a dendrogram object.
ad <- as.dendrogram(a)
# dend_data contains segment information
library(ggdendro)
dend_data <- dendro_data(ad, type = "rectangle")
nodes <- dend_data$segments
# Append z value of 0 so that the dendrogram lies in a 2D plane embedded in 3D space.
nodes_3d <- cbind(nodes, z = 0, zend = 0)
nodes_3d <- nodes_3d[,c(1, 2, 5, 3, 4, 6)]
# Convert nodes_3d to nodes_3dLong, which is used by segments3d function to draw lines.
colnames(nodes_3d) <- NULL
nodes_3da <- nodes_3d[,1:3]
nodes_3db <- nodes_3d[,4:6]
nodes_3dLong <- do.call(rbind, lapply(1:nrow(nodes_3d),
function(i) rbind(unlist(c(nodes_3da[i,])),
unlist(c(nodes_3db[i,])))))
# Plot the dendrogram in 3D.
library(rgl)
open3d()
segments3d(nodes_3dLong)
The above code (fully reproducible) produces dendrogram in 3D space in rgl window. I want to convert this dendrogram to polar dendrogram in rgl window. The polar dendrogram should still lie in a 2D plane in 3D space. The only difference is that it is a polar dendrogram. For 2D images, coord_polar in ggplot2 is used to create polar dendrogram. But I do not know how to do this in 3D.
P.S. After converting to 3D polar dendrograms, I want to add 3D meshes at specified position through translate3d. So I wish any solution open to the possibility of further editing this 3D polar dendrogram by adding new 3D meshes. Thank you.
Here's some code that converts the nodes
variable from your calculation by adding polar coordinates to it, and then draws the tree using that:
a <- list() # initialize empty object
# define merging pattern:
# negative numbers are leaves,
# positive are merged clusters (defined by row number in $merge)
a$merge <- matrix(c(-1, -2,
-3, -4,
1, 2), nc=2, byrow=TRUE )
a$height <- c(1, 1.5, 3) # define merge heights
a$order <- 1:4 # order of leaves(trivial if hand-entered)
a$labels <- LETTERS[1:4] # labels of leaves
class(a) <- "hclust" # make it an hclust object
plot(a) # show it
# Convert to a dendrogram object.
ad <- as.dendrogram(a)
# dend_data contains segment information
library(ggdendro)
dend_data <- dendro_data(ad, type = "rectangle")
nodes <- dend_data$segments
# Set the gap between the ends of the tree
gap <- 0
# Set the offset from the center.
offset <- 0
radius <- with(nodes, max(c(y, yend)) + offset)
circ <- with(nodes, max(c(x, xend)) + gap)
# Convert to polar coordinates
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)
# Extract the horizontal and vertical segments
horiz <- subset(nodes, y == yend)
vert <- subset(nodes, x == xend)
library(rgl)
open3d()
#> glX
#> 1
# Draw the vertical segments, which are still segments
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)
# Draw the horizontal segments, which are now arcs. Zero
# radius arcs are dropped
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)))
# Draw the labels
labels <- dend_data$labels
labels$theta <- with(labels, 2*pi*x/circ)
# Add a bit to the y so the label doesn't overlap the segment
labels$r <- with(labels, (radius - y)/radius + 0.1)
with(labels, text3d(r*cos(theta), r*sin(theta), 0, label))
# Draw a circle around the whole thing
margin <- 0.25 # The gap below the leaves
theta <- seq(from = 0, to = 2*pi, length = 50)
r <- 1 + margin
lines3d(r*cos(theta), r*sin(theta), 0)
Created on 2023-04-12 with reprex v2.0.2
The positioning of the labels should give you a hint as to how to position your meshes.
BTW, there's one possible bug in this code: if any of the arcs go more than half-way around the circle, they'll be drawn in the wrong direction. If this is a problem, break up those pieces into several arcs.