hypertree = function(ans, hres = 50., xsh = 0.04, drop = (max(h) - min(h))/12., labspace = drop/1.9 + padx, padx = 0.05, cex = 0.8, xline, lab, titlecex = 1.15) { # This algorithm was originally written with # y as merge height # x as groups id # and then transposed # The notation is not consistent x = match(1.:length(ans$order), ans$order) h = ans$height join = ans$merge join.n = nrow(join) ngrp.x = rep(NA, join.n) ngrp.y = h if(missing(lab)) lab = as.character(1.:(join.n + 1.)) rx = c(min(h) - drop - labspace, max(h) + padx) ry = c(0.5, length(x) + 0.5) panelScale(rx = rx, ry = ry) if(missing(xline)) { xline = panelInbounds(rx) xline = xline[xline > min(h)] } axis(side=1,at=xline,tck=1,labels=F, col = "#AFAFAF") axis(side=1,at=xline,tck=0,mgp = c(1,.1,0),cex.axis=cex) mtext(side=1,line=1.2,"Merge Height",cex=cex) mtext(side=3, line=.5, "Cluster Tree", cex=titlecex) grp1 = join[, 1] grp2 = join[, 2] for(i in 1.:join.n) { g1 = grp1[i] g2 = grp2[i] if(g1 < 0.) { g1 = abs(g1) grp1.x = x[g1] grp1.y = h[i] - drop text(grp1.y - xsh, grp1.x, lab[g1], adj = 1., cex = cex, col = 1.) } else { grp1.x = ngrp.x[g1] grp1.y = ngrp.y[g1] } if(g2 < 0.) { g2 = abs(g2) grp2.x = x[g2] grp2.y = h[i] - drop text(grp2.y - xsh, grp2.x, lab[g2], adj = 1., cex = cex, col = 1.) } else { grp2.x = ngrp.x[g2] grp2.y = ngrp.y[g2] } ym = h[i] k1 = (grp1.y/ym - 2.)^2. k2 = (grp2.y/ym - 2.)^2. c1 = ifelse(k1 > 1., - sqrt(k1 - 1.), - sqrt(1. - k1)) c2 = ifelse(k2 > 1., sqrt(k2 - 1.), sqrt(1. - k2)) xm = (c1 * grp2.x - c2 * grp1.x)/(c1 - c2) # save the new peak #ngrp.x[i] = (grp1.x+grp2.x)/2 b = (grp2.x - xm)/c2 # plot the hyperbola ngrp.x[i] = xm px = seq(grp1.x, grp2.x, length = hres) py = - ym * sqrt(1. + ((px - xm)/b)^2.) + 2. * ym #segments(ngrp.y[i],grp1.x,ngrp.y[i],grp2.x) #segments(grp1.x,ngrp.y[i],grp1.x,grp1.y) #segments(grp2.x,ngrp.y[i],grp2.x,grp2.y) # plot the lines lines(py, px, col = "#FF0000", lwd = 2) } panelOutline(col = "#000000") }