Browse code

Added layout.method parameter to ggtree.R to modify behaviour of layout method (unrooted). Modified method-fortify.R to accommodate layout.method parameter. Refactored and added various functions to tidytree.R for the "daylight" layout algorithm for unrooted trees.

JustGitting authored on 06/04/2017 04:42:40
Showing 3 changed files

... ...
@@ -5,6 +5,7 @@
5 5
 ##' @param tr phylo object
6 6
 ##' @param mapping aes mapping
7 7
 ##' @param layout one of 'rectangular', 'slanted', 'fan', 'circular', 'radial' or 'unrooted'
8
+##' @param layout.method of 'equal_angle', 'daylight'.
8 9
 ##' @param open.angle open angle, only for 'fan' layout
9 10
 ##' @param mrsd most recent sampling date
10 11
 ##' @param as.Date logical whether using Date class in time tree
... ...
@@ -33,6 +34,7 @@
33 34
 ggtree <- function(tr,
34 35
                    mapping        = NULL,
35 36
                    layout         = "rectangular",
37
+                   layout.method  = "equal_angle",
36 38
                    open.angle     = 0,
37 39
                    mrsd           = NULL,
38 40
                    as.Date        = FALSE,
... ...
@@ -44,8 +46,10 @@ ggtree <- function(tr,
44 46
                    ndigits        = NULL,
45 47
                    ...) {
46 48
 
49
+    # Check if layout string is valid.
47 50
     layout %<>% match.arg(c("rectangular", "slanted", "fan", "circular", "radial", "unrooted"))
48
-
51
+    layout.method %<>% match.arg(c("equal_angle", "daylight"))
52
+  
49 53
     if (is(tr, "r8s") && branch.length == "branch.length") {
50 54
         branch.length = "TREE"
51 55
     }
... ...
@@ -60,8 +64,11 @@ ggtree <- function(tr,
60 64
     } else {
61 65
         mapping <- modifyList(aes_(~x, ~y), mapping)
62 66
     }
63
-    p <- ggplot(tr, mapping=mapping,
67
+  
68
+    p <- ggplot(tr, 
69
+                mapping       = mapping,
64 70
                 layout        = layout,
71
+                layout.method = layout.method,
65 72
                 mrsd          = mrsd,
66 73
                 as.Date       = as.Date,
67 74
                 yscale        = yscale,
... ...
@@ -110,6 +110,7 @@ rm.singleton.newick <- function(nwk, outfile = NULL) {
110 110
 ##' @export
111 111
 fortify.beast <- function(model, data,
112 112
                           layout        = "rectangular",
113
+                          layout.method = "equal_angle",
113 114
                           yscale        = "none",
114 115
                           ladderize     = TRUE,
115 116
                           right         = FALSE,
... ...
@@ -119,7 +120,8 @@ fortify.beast <- function(model, data,
119 120
 
120 121
     model <- set_branch_length(model, branch.length)
121 122
     phylo <- model@phylo
122
-    df    <- fortify(phylo, layout=layout, branch.length=branch.length,
123
+    df    <- fortify(phylo, layout=layout, layout.method=layout.method,
124
+                     branch.length=branch.length,
123 125
                      ladderize=ladderize, right=right, mrsd = mrsd, ...)
124 126
 
125 127
     stats <- model@stats
... ...
@@ -220,6 +222,7 @@ fortify.beast <- function(model, data,
220 222
 ##' @export
221 223
 fortify.codeml <- function(model, data,
222 224
                            layout        = "rectangular",
225
+                           layout.method = "equal_angle",
223 226
                            yscale        = "none",
224 227
                            ladderize     = TRUE,
225 228
                            right         = FALSE,
... ...
@@ -265,6 +268,7 @@ fortify.codeml <- function(model, data,
265 268
 ##' @export
266 269
 fortify.codeml_mlc <- function(model, data,
267 270
                                layout        = "rectangular",
271
+                               layout.method = "equal_angle",
268 272
                                yscale        = "none",
269 273
                                ladderize     = TRUE,
270 274
                                right         = FALSE,
... ...
@@ -306,6 +310,7 @@ merge_phylo_anno.codeml_mlc <- function(df, dNdS, ndigits = NULL) {
306 310
 
307 311
 fortify.codeml_mlc_ <- function(model, data,
308 312
                                 layout        = "rectangular",
313
+                                layout.method = "equal_angle",
309 314
                                 ladderize     = TRUE,
310 315
                                 right         = FALSE,
311 316
                                 branch.length = "branch.length",
... ...
@@ -317,8 +322,12 @@ fortify.codeml_mlc_ <- function(model, data,
317 322
 
318 323
 ##' @method fortify paml_rst
319 324
 ##' @export
320
-fortify.paml_rst <- function(model, data, layout = "rectangular", yscale="none",
321
-                             ladderize=TRUE, right=FALSE, mrsd=NULL, ...) {
325
+fortify.paml_rst <- function(model, data, 
326
+                             layout = "rectangular", 
327
+                             yscale="none",
328
+                             ladderize=TRUE, 
329
+                             right=FALSE, 
330
+                             mrsd=NULL, ...) {
322 331
     df <- fortify.phylo(model@phylo, data, layout, ladderize, right, mrsd=mrsd, ...)
323 332
     df <- merge_phylo_anno.paml_rst(df, model)
324 333
     df <- scaleY(model@phylo, df, yscale, layout, ...)
... ...
@@ -351,8 +360,11 @@ fortify.hyphy <- fortify.paml_rst
351 360
 ##' @importFrom treeio get.placements
352 361
 ##' @export
353 362
 fortify.jplace <- function(model, data,
354
-                           layout="rectangular", yscale="none",
355
-                           ladderize=TRUE, right=FALSE, mrsd=NULL, ...) {
363
+                           layout="rectangular",
364
+                           yscale="none",
365
+                           ladderize=TRUE, 
366
+                           right=FALSE, 
367
+                           mrsd=NULL, ...) {
356 368
     df <- extract.treeinfo.jplace(model, layout, ladderize, right, mrsd=mrsd, ...)
357 369
     place <- get.placements(model, by="best")
358 370
 
... ...
@@ -401,8 +413,13 @@ fortify.phylo4 <- function(model, data, layout="rectangular", yscale="none",
401 413
 
402 414
 ##' @method fortify phylo4d
403 415
 ##' @export
404
-fortify.phylo4d <- function(model, data, layout="rectangular", yscale="none",
405
-                            ladderize=TRUE, right=FALSE, branch.length="branch.length",
416
+fortify.phylo4d <- function(model, data, 
417
+                            layout="rectangular",
418
+                            layout.method = "equal_angle",
419
+                            yscale="none",
420
+                            ladderize=TRUE, 
421
+                            right=FALSE, 
422
+                            branch.length="branch.length",
406 423
                             mrsd=NULL, ...) {
407 424
     ## model <- set_branch_length(model, branch.length)
408 425
     ## phylo <- as.phylo.phylo4(model)
... ...
@@ -411,7 +428,7 @@ fortify.phylo4d <- function(model, data, layout="rectangular", yscale="none",
411 428
     ## tdata <- model@data[match(res$node, rownames(model@data)), , drop=FALSE]
412 429
     ## df <- cbind(res, tdata)
413 430
     ## scaleY(as.phylo.phylo4(model), df, yscale, layout, ...)
414
-    fortify(as.treedata(model), data, layout, yscale, ladderize, right, branch.length, mrsd, ...)
431
+    fortify(as.treedata(model), data, layout, layout.method, yscale, ladderize, right, branch.length, mrsd, ...)
415 432
 }
416 433
 
417 434
 
... ...
@@ -436,8 +453,12 @@ fortify.phylo4d <- function(model, data, layout="rectangular", yscale="none",
436 453
 ##' @method fortify phylo
437 454
 ##' @export
438 455
 ##' @author Yu Guangchuang
439
-fortify.phylo <- function(model, data, layout="rectangular",
440
-                          ladderize=TRUE, right=FALSE, mrsd=NULL, as.Date=FALSE, ...) {
456
+fortify.phylo <- function(model, data, 
457
+                          layout="rectangular",
458
+                          ladderize=TRUE, 
459
+                          right=FALSE, 
460
+                          mrsd=NULL, 
461
+                          as.Date=FALSE, ...) {
441 462
     tree <- reorder.phylo(model, 'postorder')
442 463
 
443 464
     if (ladderize == TRUE) {
... ...
@@ -603,18 +624,26 @@ fortify.multiPhylo <-  function(model, data, layout="rectangular",
603 624
 
604 625
 ##' @method fortify phylip
605 626
 ##' @export
606
-fortify.phylip <- function(model, data, layout="rectangular",
607
-                        ladderize=TRUE, right=FALSE,
608
-                        branch.length = "TREE", mrsd=NULL, ...) {
627
+fortify.phylip <- function(model, data, 
628
+                           layout="rectangular",
629
+                           layout.method = "equal_angle",
630
+                           ladderize=TRUE, 
631
+                           right=FALSE,
632
+                           branch.length = "TREE", 
633
+                           mrsd=NULL, ...) {
609 634
     trees <- get.tree(model)
610 635
     fortify(trees, layout=layout, ladderize = ladderize, right=right, mrsd=mrsd, ...)
611 636
 }
612 637
 
613 638
 ##' @method fortify r8s
614 639
 ##' @export
615
-fortify.r8s <- function(model, data, layout="rectangular",
616
-                        ladderize=TRUE, right=FALSE,
617
-                        branch.length = "TREE", mrsd=NULL, ...) {
640
+fortify.r8s <- function(model, data, 
641
+                        layout="rectangular",
642
+                        layout.method = "equal_angle",
643
+                        ladderize=TRUE, 
644
+                        right=FALSE,
645
+                        branch.length = "TREE", 
646
+                        mrsd=NULL, ...) {
618 647
     trees <- get.tree(model)
619 648
     branch.length %<>% match.arg(names(trees))
620 649
     phylo <- trees[[branch.length]]
... ...
@@ -623,8 +652,11 @@ fortify.r8s <- function(model, data, layout="rectangular",
623 652
 
624 653
 ##' @method fortify obkData
625 654
 ##' @export
626
-fortify.obkData <- function(model, data, layout="rectangular",
627
-                            ladderize=TRUE, right=FALSE, mrsd = NULL, ...) {
655
+fortify.obkData <- function(model, data, 
656
+                            layout="rectangular",
657
+                            ladderize=TRUE, 
658
+                            right=FALSE, 
659
+                            mrsd = NULL, ...) {
628 660
 
629 661
     df <- fortify(model@trees[[1]], layout=layout, ladderize=ladderize, right=right, mrsd=mrsd, ...)
630 662
 
... ...
@@ -642,8 +674,11 @@ fortify.obkData <- function(model, data, layout="rectangular",
642 674
 
643 675
 ##' @method fortify phyloseq
644 676
 ##' @export
645
-fortify.phyloseq <- function(model, data, layout="rectangular",
646
-                             ladderize=TRUE, right=FALSE, mrsd=NULL, ...) {
677
+fortify.phyloseq <- function(model, data, 
678
+                             layout="rectangular",
679
+                             ladderize=TRUE, 
680
+                             right=FALSE, 
681
+                             mrsd=NULL, ...) {
647 682
 
648 683
     df <- fortify(model@phy_tree, layout=layout, ladderize=ladderize, right=right, mrsd=mrsd, ...)
649 684
     phyloseq <- "phyloseq"
... ...
@@ -159,79 +159,625 @@ reroot_node_mapping <- function(tree, tree2) {
159 159
 
160 160
 
161 161
 ##' @importFrom ape reorder.phylo
162
-layout.unrooted <- function(tree, branch.length="branch.length", ...) {
163
-    N <- getNodeNum(tree)
164
-    root <- getRoot(tree)
162
+layout.unrooted <- function(tree, branch.length="branch.length", layout.method="equal.angle", ...) {
165 163
 
166
-    df <- as.data.frame.phylo_(tree)
167
-    df$x <- NA
168
-    df$y <- NA
169
-    df$start <- NA
170
-    df$end   <- NA
171
-    df$angle <- NA
172
-    df[root, "x"] <- 0
173
-    df[root, "y"] <- 0
174
-    df[root, "start"] <- 0
175
-    df[root, "end"]   <- 2
176
-    df[root, "angle"] <- 0
164
+    switch(layout.method,
165
+           equal_angle = { df <- layoutEqualAngle(tree, branch.length) },
166
+           daylight = { df <- layoutDaylight(tree, branch.length) }
167
+    )
177 168
 
178
-    nb.sp <- sapply(1:N, function(i) length(get.offspring.tip(tree, i)))
169
+    return(df)
170
+}
179 171
 
180
-    nodes <- getNodes_by_postorder(tree)
181 172
 
182
-    for(curNode in nodes) {
183
-        curNtip <- nb.sp[curNode]
184
-        children <- getChild(tree, curNode)
173
+##' 'Equal-angle layout algorithm for unrooted trees'
174
+##'
175
+##' @references 
176
+##' "Inferring Phylogenies" by Joseph Felsenstein.
177
+##' 
178
+##' @title layoutEqualAngle
179
+##' @param tree phylo object
180
+##' @param branch.length set to 'none' for edge length of 1. Otherwise the phylogenetic tree edge length is used.
181
+##' @return tree as data.frame with equal angle layout.
182
+layoutEqualAngle <- function(tree, branch.length ){
183
+  root <- getRoot(tree)
184
+  # Convert Phylo tree to data.frame.
185
+  df <- as.data.frame.phylo_(tree)
186
+  
187
+  # NOTE: Angles (start, end, angle) are in half-rotation units (radians/pi or degrees/180)
188
+  
189
+  # create and assign NA to the following fields.
190
+  df$x <- NA
191
+  df$y <- NA
192
+  df$start <- NA # Start angle of segment of subtree.
193
+  df$end   <- NA # End angle of segment of subtree
194
+  df$angle <- NA # Orthogonal angle to beta ... for labels??
195
+  # Initialize root node position and angles.
196
+  df[root, "x"] <- 0
197
+  df[root, "y"] <- 0
198
+  df[root, "start"] <- 0 # 0-degrees
199
+  df[root, "end"]   <- 2 # 360-degrees
200
+  df[root, "angle"] <- 0 # Angle label.
201
+  
202
+  N <- getNodeNum(tree)
203
+
204
+  # Get number of tips for each node in tree.
205
+  nb.sp <- sapply(1:N, function(i) length(get.offspring.tip(tree, i)))
206
+  # Get list of node id's.
207
+  nodes <- getNodes_by_postorder(tree)
208
+  
209
+  for(curNode in nodes) { 
210
+    # Get number of tips for current node.
211
+    curNtip <- nb.sp[curNode]
212
+    # Get array of child node indexes of current node.
213
+    children <- getChild(tree, curNode)
214
+    
215
+    # Get "start" and "end" angles of a segment for current node in the data.frame.
216
+    start <- df[curNode, "start"]
217
+    end <- df[curNode, "end"]
218
+    
219
+    if (length(children) == 0) {
220
+      ## is a tip
221
+      next
222
+    }
223
+    
224
+    for (i in seq_along(children)) {
225
+      child <- children[i]
226
+      # Get the number of tips for child node.
227
+      ntip.child <- nb.sp[child] 
228
+      
229
+      # Calculated in half radians.
230
+      # alpha: angle of segment for i-th child with ntips_ij tips.
231
+      # alpha = (left_angle - right_angle) * (ntips_ij)/(ntips_current)
232
+      alpha <- (end - start) * ntip.child / curNtip
233
+      # beta = angle of line from parent node to i-th child.
234
+      beta <- start + alpha / 2
235
+      
236
+      if (branch.length == "none") {
237
+        length.child <- 1
238
+      } else {
239
+        length.child <- df[child, "length"]
240
+      }
241
+      
242
+      # update geometry of data.frame.
243
+      # Calculate (x,y) position of the i-th child node from current node.
244
+      df[child, "x"] <- df[curNode, "x"] + cospi(beta) * length.child
245
+      df[child, "y"] <- df[curNode, "y"] + sinpi(beta) * length.child
246
+      # Calculate orthogonal angle to beta. 
247
+      df[child, "angle"] <- -90 - 180 * beta * sign(beta - 1)
248
+      # Update the start and end angles of the childs segment.
249
+      df[child, "start"] <- start
250
+      df[child, "end"] <- start + alpha
251
+      start <- start + alpha
252
+    } 
253
+
254
+  }
255
+  
256
+  return(df)
257
+  
258
+}
185 259
 
186
-        start <- df[curNode, "start"]
187
-        end <- df[curNode, "end"]
260
+##' Equal daylight layout method for unrooted trees.
261
+##' 
262
+##' #' @title
263
+##' @param tree phylo object
264
+##' @param branch.length set to 'none' for edge length of 1. Otherwise the phylogenetic tree edge length is used.
265
+##' @return tree as data.frame with equal angle layout.
266
+##' @references 
267
+##' The following aglorithm aims to implement the vague description of the "Equal-daylight Algorithm"
268
+##' in "Inferring Phylogenies" pp 582-584 by Joseph Felsenstein.
269
+##' 
270
+##' ```
271
+##' Leafs are subtrees with no children
272
+##' Initialise tree using equal angle algorithm
273
+##' tree_df = equal_angle(tree)
274
+##'
275
+##' nodes = get list of nodes in tree_df breadth-first
276
+##' nodes = remove tip nodes.
277
+##' 
278
+##' ```
279
+layoutDaylight <- function( tree, branch.length ){
280
+
281
+  # How to set optimal
282
+  MAX_COUNT <- 100
283
+  MINIMUM_AVERAGE_ANGLE_CHANGE <- 0.01
284
+  
285
+
286
+  # Initialize tree.
287
+  tree_df <- layoutEqualAngle(tree, branch.length)
288
+
289
+  # nodes = get list of nodes in tree_df
290
+  # Get list of node id's.
291
+  #nodes <- getNodes_by_postorder(tree)
292
+  # nodes <- GetSubtree.df(tree_df, root)
293
+  
294
+  # Get list of internal nodes
295
+  #nodes <- tree_df[tree_df$IsTip != TRUE]$nodes
296
+  
297
+  nodes <- getNodesBreadthFirst.df(tree_df)
298
+  # select only internal nodes
299
+  internal_nodes <- tree_df[!tree_df$isTip,]$node
300
+  # Remove tips from nodes list, but keeping order.
301
+  nodes <- intersect(nodes, internal_nodes)
302
+  
303
+  i <- 1
304
+  ave_change <- 1.0
305
+  while( i <= MAX_COUNT & ave_change > MINIMUM_AVERAGE_ANGLE_CHANGE ){
306
+    cat('Iteration: ', i, '\n')
307
+
308
+    # Reset max_change after iterating over tree.
309
+    total_max <- 0.0
310
+    
311
+    # for node in nodes {
312
+    for( j in seq_along(nodes)){
313
+      currentNode_id <- nodes[j]
314
+      
315
+      result <- applyLayoutDaylight(tree_df, currentNode_id)
316
+      tree_df <- result$tree
317
+      total_max <- total_max + result$max_change
318
+      
319
+    }
320
+    
321
+    ave_change <- total_max / length(nodes)
322
+    
323
+    cat('Average angle change', ave_change,'\n')
324
+    
325
+    i <- i + 1
326
+  }
327
+  
328
+  return(tree_df)
329
+  
330
+}
188 331
 
189
-        if (length(children) == 0) {
190
-            ## is a tip
191
-            next
332
+##' Apply the daylight alorithm to adjust the spacing between the subtrees and tips of the
333
+##' specified node.
334
+##' 
335
+##' @title applyLayoutDaylight
336
+##' @param df tree data.frame
337
+##' @param node_id is id of the node from which daylight is measured to the other subtrees.
338
+##' @return list with tree data.frame with updated layout using daylight algorithm and max_change angle .
339
+##' 
340
+##'
341
+##' ```
342
+##' for node in nodes {
343
+##'   if node is a leaf {
344
+##'     next
345
+##'   }
346
+##'   
347
+##'   subtrees = get subtrees of node
348
+##'   
349
+##'   for i-th subtree in subtrees {
350
+##'     [end, start] = get left and right angles of tree from node id.
351
+##'     angle_list[i, 'left'] = end
352
+##'     angle_list[i, 'beta'] = start - end  # subtree arc angle
353
+##'     angle_list[i, 'index'] = i-th # index of subtree/leaf 
354
+##'   }
355
+##'
356
+##'   sort angle_list by 'left' column in ascending order.
357
+##'
358
+##'   D = 360 - sum( angle_list['beta'] ) # total daylight angle
359
+##'   d = D / |subtrees| # equal daylight angle.
360
+##' 
361
+##'   new_L = left angle of first subtree.
362
+##'
363
+##'   for n-th row in angle_list{
364
+##'     # Calculate angle to rotate subtree/leaf to create correct daylight angle.
365
+##'     new_left_angle = new_left_angle + d + angle_list[n, 'beta']
366
+##'     Calculate the difference between the old and new left angles.
367
+##'     adjust_angle = new_left_angle - angle_list[n, 'left'] 
368
+##'     
369
+##'     index = angle_list['index']
370
+##'     rotate subtree[index] wrt n-th node by adjust_angle
371
+##'     }
372
+##'   }
373
+##' }
374
+##' ```
375
+applyLayoutDaylight <- function(df, node_id ){
376
+  
377
+  max_change <- 0.0
378
+  
379
+  # Get lists of node ids for each subtree, including  rest of unrooted tree.
380
+  subtrees <- getSubtreeUnrooted.df(df, node_id)
381
+  angle_list <- data.frame(left=numeric(0), beta=numeric(0), subtree_id=integer(0) )
382
+
383
+  # Return tree if only 2 or less subtrees to adjust.
384
+  if(length(subtrees) <= 2){
385
+    return( list(tree = df, max_change = max_change) )
386
+  }
387
+  
388
+  # Find start and end angles for each subtree.
389
+  #   subtrees = get subtrees of node
390
+  #   for i-th subtree in subtrees {
391
+  for (i in seq_along(subtrees) ) {
392
+    subtree <- subtrees[[i]]
393
+    # [end, start] = get start and end angles of tree.
394
+    
395
+    angles <- getTreeArcAngles(df, node_id, subtree)
396
+    angle_list[ i, 'subtree_id'] <- i
397
+    angle_list[ i, 'left'] <- angles['left']
398
+    angle_list[ i, 'beta'] <- angles['left'] - angles['right'] # subtree arc angle
399
+    # If subtree arc angle is -ve, then + 2 (360).
400
+    if(angle_list[ i, 'beta'] < 0 ){
401
+      angle_list[ i, 'beta'] <- angle_list[ i, 'beta'] + 2
402
+    } 
403
+  }
404
+  #   sort angle_list by 'left angle' column in ascending order.
405
+  angle_list <- angle_list[with(angle_list, order(left)), ]
406
+  #   D = 360 - sum( angle_list['beta'] ) # total day
407
+  #   d = D / |subtrees| # equal daylight angle.
408
+  total_daylight <- 2 - colSums(angle_list['beta']) 
409
+  d <- total_daylight / length(subtrees)
410
+   
411
+  # Initialise new left-angle as first subtree left-angle.
412
+  new_left_angle <- angle_list[1, 'left']
413
+
414
+  # Adjust angles of subtrees and tips connected to current node.
415
+  # for n-th row in angle_list{
416
+  # Skip the first subtree as it is not adjusted.
417
+  for (i in 2:nrow(angle_list) ) {
418
+    # Calculate angle to rotate subtree/leaf to create correct daylight angle.
419
+    new_left_angle <- new_left_angle + d + angle_list[i, 'beta']
420
+    # Calculate the difference between the old and new left angles.
421
+    adjust_angle <- new_left_angle - angle_list[i, 'left']
422
+    
423
+    max_change <- max(max_change, abs(adjust_angle))
424
+    #cat('Adjust angle:', abs(adjust_angle), ' Max change:', max_change ,'\n')
425
+    
426
+    # rotate subtree[index] wrt current node
427
+    subtree_id <- angle_list[i, 'subtree_id']
428
+    subtree_nodes <- subtrees[[subtree_id]]$subtree
429
+    # update tree_df for all subtrees with rotated points.
430
+    df <- rotateTreePoints.df(df, node_id, subtree_nodes, adjust_angle)
431
+  }
432
+  
433
+  return( list(tree = df, max_change = max_change) ) 
434
+  
435
+}
436
+
437
+
438
+##' Find the right (clockwise rotation, angle from +ve x-axis to furthest subtree nodes) and 
439
+##' left (anti-clockwise angle from +ve x-axis to subtree)
440
+##' 
441
+##' @title getTreeArcAngles
442
+##' @param df tree data.frame
443
+##' @param origin_id node id from which to calculate left and right hand angles of subtree.
444
+##' @param subtree named list of root id of subtree and list of node ids for given subtree.
445
+##' @return named list with right and left angles in range [0,2] i.e 1 = 180 degrees, 1.5 = 270 degrees.
446
+getTreeArcAngles <- function(df, origin_id, subtree) {
447
+  # Initialise variables
448
+  theta_child <- 0.0
449
+  subtree_root_id <- subtree$node
450
+  subtree_node_ids <- subtree$subtree
451
+  
452
+  # Initialise angle from origin node to parent node.
453
+  # If subtree_root_id is child of origin_id
454
+  if( any(subtree_root_id == getChild.df(df, origin_id)) ){
455
+    theta_parent <- getNodeAngle.df(df, origin_id, subtree_root_id)
456
+  }else{
457
+    # get the real root of df tree to initialise left and right angles.
458
+    theta_parent <- getNodeAngle.df(df, origin_id, getRoot.df(df))
459
+  }
460
+    
461
+  # create vector with named columns
462
+  # left-hand and right-hand angles between origin node and the extremities of the tree nodes.
463
+  arc <- c('left' = theta_parent, 'right' = theta_parent)
464
+    
465
+  # Subtree has to have 1 or more nodes to compare.
466
+  if (length(subtree_node_ids) == 0 ){
467
+    return(0)
468
+  } 
469
+
470
+  
471
+  # Remove tips from nodes list, but keeping order.
472
+  # internal_nodes <- df[!df$isTip,]$node
473
+  # subtree_node_ids <- intersect(subtree_node_ids, internal_nodes)
474
+  
475
+  
476
+  # Calculate the angle from the origin node to each child node.
477
+  # Moving from parent to children in depth-first traversal.
478
+  for( i in seq_along(subtree_node_ids) ){
479
+    parent_id <- subtree_node_ids[i]
480
+    # Get angle from origin node to parent node.
481
+    # Skip if parent_id is a tip.
482
+    if(isTip.df(df, parent_id) ){ next }
483
+
484
+    theta_parent <- getNodeAngle.df(df, origin_id, parent_id)
485
+  
486
+    children_ids <- getChild.df(df, parent_id)
487
+    
488
+    for( j in seq_along(children_ids)){
489
+      #delta_x <- df[subtree_node_id, 'x'] - df[origin_id, 'x']
490
+      #delta_y <- df[subtree_node_id, 'y'] - df[origin_id, 'y']
491
+      #angles[i] <- atan2(delta_y, delta_x) / pi
492
+      child_id <- children_ids[j]
493
+      # Skip if child is parent node of subtree.
494
+      if( child_id == origin_id ){
495
+        next
496
+      }
497
+      
498
+      theta_child <- getNodeAngle.df(df, origin_id, child_id)
499
+
500
+      # Skip if child node is already inside arc.
501
+      # if left < right angle (arc crosses 180/-180 quadrant) and child node is not inside arc of tree.
502
+      # OR if left > right angle (arc crosses 0/360 quadrant) and child node is inside gap
503
+      if ( (arc['left'] < arc['right'] & !( theta_child > arc['left'] & theta_child < arc['right'])) |
504
+        (arc['left'] > arc['right'] & ( theta_child < arc['left'] & theta_child > arc['right'])) ){
505
+        # child node inside arc.
506
+        next
507
+      }
508
+      
509
+      
510
+      delta <- theta_child - theta_parent
511
+      delta_adj <- delta
512
+      # Correct the delta if parent and child angles cross the 180/-180 half of circle.
513
+      # If delta > 180
514
+      if( delta > 1){ # Edge between parent and child cross upper and lower quadrants of cirlce on 180/-180 side.
515
+        delta_adj <- delta - 2 # delta' = delta - 360
516
+      # If delta < -180
517
+      }else if( delta < -1){ # Edge between parent and child cross upper and lower quadrants of cirlce
518
+        delta_adj <- delta + 2 # delta' = delta - 360
519
+      }
520
+      
521
+      theta_child_adj <- theta_child
522
+
523
+      # If angle change from parent to node is positive (anti-clockwise), check left angle
524
+      if(delta_adj > 0){
525
+        # If child/parent edges cross the -180/180 quadrant (angle between them is > 180), 
526
+        # check if right angle and child angle are different signs and adjust if needed.
527
+        if( abs(delta) > 1){
528
+          if( arc['left'] > 0 & theta_child < 0){
529
+            theta_child_adj <- theta_child + 2
530
+          }else if (arc['left'] < 0 & theta_child > 0){
531
+            theta_child_adj <- theta_child - 2
532
+          }
533
+        }
534
+        
535
+          # check if left angle of arc is less than angle of child. Update if true.
536
+        if( arc['left'] < theta_child_adj ){
537
+          arc['left'] <- theta_child
192 538
         }
539
+      # If angle change from parent to node is negative (clockwise), check right angle  
540
+      }else if(delta_adj < 0){
541
+        # If child/parent edges cross the -180/180 quadrant (angle between them is > 180), 
542
+        # check if right angle and child angle are different signs and adjust if needed.
543
+        if( abs(delta) > 1){
544
+          # Else change in angle from parent to child is negative, then adjust child angle if right angle is a different sign.
545
+          if( arc['right'] > 0 & theta_child < 0){
546
+            theta_child_adj <- theta_child + 2
547
+          }else if (arc['right'] < 0 & theta_child > 0){
548
+            theta_child_adj <- theta_child - 2
549
+          }
550
+        }
551
+        # check if right angle of arc is greater than angle of child. Update if true.
552
+        if( arc['right'] > theta_child_adj  ){
553
+          arc['right'] <- theta_child
554
+        }  
555
+        
556
+      }
557
+    }
193 558
 
194
-        for (i in seq_along(children)) {
195
-            child <- children[i]
196
-            ntip.child <- nb.sp[child]
559
+  } 
560
+  # Convert arc angles of [1, -1] to [2,0] domain.
561
+  arc[arc<0] <- arc[arc<0] + 2
562
+  return(arc)
563
+  
564
+}
197 565
 
198
-            alpha <- (end - start) * ntip.child/curNtip
199
-            beta <- start + alpha / 2
566
+##' Rotate the points in a tree data.frame around a pivot node by the angle specified.
567
+##' 
568
+##' @title rotateTreePoints.data.fram
569
+##' @param df tree data.frame
570
+##' @param pivot_node is the id of the pivot node.
571
+##' @param nodes list of node numbers that are to be rotated by angle around the pivot_node
572
+##' @param angle in range [0,2], ie degrees/180, radians/pi
573
+##' @return updated tree data.frame with points rotated by angle
574
+rotateTreePoints.df <- function(df, pivot_node, nodes, angle){
575
+  # Rotate nodes around pivot_node.
576
+  # x' = cos(angle)*delta_x - sin(angle)*delta_y + delta_x
577
+  # y' = sin(angle)*delta_x + cos(angle)*delta_y + delta_y
578
+  
579
+  cospitheta <- cospi(angle)
580
+  sinpitheta <- sinpi(angle)
581
+  for(node in nodes){
582
+    # Update (x,y) of node
583
+    delta_x <- df[node, 'x'] - df[pivot_node, 'x']
584
+    delta_y <- df[node, 'y'] - df[pivot_node, 'y']
585
+    df[node, 'x'] <- cospitheta * delta_x - sinpitheta * delta_y + df[pivot_node, 'x']
586
+    df[node, 'y'] <- sinpitheta * delta_x + cospitheta * delta_y + df[pivot_node, 'y']
587
+    
588
+    # Update label angle if not root node.
589
+    # get parent
590
+    parent_id <- getParent.df(df, node)
591
+    # if 'node' is not root, then update label angle.
592
+    if( parent_id != 0){
593
+      theta_parent_child <- getNodeAngle.df(df, parent_id, node)
594
+      if(!is.na(theta_parent_child)){
595
+        # Update label angle
596
+        df[node, 'angle'] <- -90 - 180 * theta_parent_child * sign(theta_parent_child - 1)
597
+      }
598
+    }
599
+    
600
+  }
601
+  return(df)
602
+}
200 603
 
201
-            if (branch.length == "none") {
202
-                length.child <- 1
203
-            } else {
204
-                length.child <- df[child, "length"]
205
-            }
604
+##' Get the angle between the two nodes specified.
605
+##' 
606
+##' @title getNodeAngle.df
607
+##' @param df tree data.frame
608
+##' @param origin_node_id origin node id number
609
+##' @param node_id end node id number
610
+##' @return angle in range [-1, 1], i.e. degrees/180, radians/pi
611
+getNodeAngle.df <- function(df, origin_node_id, node_id){
612
+  if(origin_node_id != node_id){
613
+    delta_x <- df[node_id, 'x'] - df[origin_node_id, 'x']
614
+    delta_y <- df[node_id, 'y'] - df[origin_node_id, 'y']
615
+    angle <- atan2(delta_y, delta_x) / pi
616
+    return( angle )
617
+  }else{
618
+    return(NA)
619
+  }
620
+}
206 621
 
207
-            df[child, "x"] <- df[curNode, "x"] + cospi(beta) * length.child
208
-            df[child, "y"] <- df[curNode, "y"] + sinpi(beta) * length.child
209
-            df[child, "angle"] <- -90 -180 * beta * sign(beta - 1)
210
-            df[child, "start"] <- start
211
-            df[child, "end"] <- start + alpha
212
-            start <- start + alpha
213
-        }
214 622
 
215
-    }
216 623
 
217
-    return(df)
624
+##' Get all children of node from tree, including start_node.
625
+##' 
626
+##' @title getSubtree
627
+##' @param tree ape phylo tree object
628
+##' @param node is the tree node id from which the tree is derived.
629
+##' @return list of all child node id's from starting node.
630
+getSubtree <- function(tree, node){
631
+  
632
+  subtree <- c(node)
633
+  i <- 1
634
+  while( i <= length(subtree)){
635
+    subtree <- c(subtree, getChild(tree, subtree[i]))
636
+    # remove any '0' root nodes
637
+    subtree <- subtree[subtree != 0]
638
+    i <- i + 1
639
+  }
640
+  return(subtree)
218 641
 }
219 642
 
643
+# Get all children of node from df tree using breath-first.
644
+#
645
+##' @title GetSubtree.df
646
+##' @param df tree data.frame
647
+##' @param node id of starting node.
648
+##' @return list of all child node id's from starting node.
649
+##' @examples
650
+GetSubtree.df <- function(df, node){
651
+  subtree <- c(node)
652
+  i <- 1
653
+  while( i <= length(subtree)){
654
+    subtree <- c(subtree, getChild.df(df, subtree[i]))
655
+    # remove any '0' root nodes
656
+    subtree <- subtree[subtree != 0]
657
+    i <- i + 1
658
+  }
659
+  return(subtree)
660
+}
661
+
662
+##' Get all subtrees of specified node. This includes all ancestors and relatives of node and 
663
+##' return named list of subtrees.
664
+##' 
665
+##' @title GetSubtreeUnrooted
666
+##' @param tree ape phylo tree object
667
+##' @param node is the tree node id from which the subtrees are derived.
668
+##' @return named list of subtrees with the root id of subtree and list of node id's making up subtree.
669
+GetSubtreeUnrooted <- function(tree, node){
670
+  # if node leaf, return nothing.
671
+  if( isTip(tree, node) ){
672
+    # return NA
673
+    return(NA)
674
+  }
675
+  
676
+  subtrees <- list()
677
+  
678
+  # get subtree for each child node.
679
+  children_ids <- getChild(tree, node)
680
+  
681
+  remaining_nodes <- getNodes_by_postorder(tree)
682
+  # Remove current node from remaining_nodes list.
683
+  remaining_nodes <- setdiff(remaining_nodes, node)
684
+  
685
+  
686
+  for( child in children_ids ){
687
+    # Append subtree nodes to list if not 0 (root).
688
+    subtree <- getSubtree(tree, child)
689
+    subtrees[[length(subtrees)+1]] <- list( node = child, subtree = subtree)
690
+    # remove subtree nodes from remaining nodes.
691
+    remaining_nodes <- setdiff(remaining_nodes, as.integer(unlist(subtrees[[length(subtrees)]]['subtree']) ))
692
+  }
693
+  
694
+  # The remaining nodes that are not found in the child subtrees are the remaining subtree nodes.
695
+  # ie, parent node and all other nodes. We don't care how they are connect, just their ids.
696
+  parent_id <- getParent(tree, node)
697
+  # If node is not root, add remainder of tree nodes as subtree.
698
+  if( parent_id != 0 & length(remaining_nodes) >= 1){
699
+    subtrees[[length(subtrees)+1]] <- list( node = parent_id, subtree = remaining_nodes)
700
+  }
701
+  
702
+  return(subtrees)
703
+}
704
+
705
+
706
+##' Get all subtrees of node, as well as remaining branches of parent (ie, rest of tree structure as subtree)
707
+##' return named list of subtrees with list name as starting node id.
708
+##' @title GetSubtreeUnrooted
709
+##' @param df tree data.frame
710
+##' @param node is the tree node id from which the subtrees are derived.
711
+##' @return named list of subtrees with the root id of subtree and list of node id's making up subtree.
712
+getSubtreeUnrooted.df <- function(df, node){
713
+  # if node leaf, return nothing.
714
+  if( isTip.df(df, node) ){
715
+    return(NA)
716
+  }
717
+  
718
+  subtrees <- list()
719
+  
720
+  # get subtree for each child node.
721
+  children_ids <- getChild.df(df, node)
722
+  
723
+  # remaining_nodes <- getNodes_by_postorder(tree)
724
+  remaining_nodes <- df$node
725
+  
726
+  # Remove current node from remaining_nodes list.
727
+  remaining_nodes <- setdiff(remaining_nodes, node)
728
+  
729
+  for( child in children_ids ){
730
+    subtree <- GetSubtree.df(df, child)
731
+    # Append subtree nodes to list if more than 1 node in subtree (i.e. not a tip)
732
+    #if(length(subtree) >= 2){
733
+      subtrees[[length(subtrees)+1]] <- list( node = child, subtree = subtree)
734
+      # remove subtree nodes from remaining nodes.
735
+      remaining_nodes <- setdiff(remaining_nodes, as.integer(unlist(subtrees[[length(subtrees)]]['subtree']) ))
736
+    #}else{
737
+      # remove remaining nodes
738
+    #  remaining_nodes <- setdiff(remaining_nodes, subtree)
739
+    #}
740
+  }
741
+  
742
+  # The remaining nodes that are not found in the child subtrees are the remaining subtree nodes.
743
+  # ie, parent node and all other nodes. We don't care how they are connected, just their id.
744
+  parent_id <- getParent.df(df, node)
745
+  # If node is not root.
746
+  if( parent_id != 0 & length(remaining_nodes) >= 1){
747
+    subtrees[[length(subtrees)+1]] <- list( node = parent_id, subtree = remaining_nodes)
748
+  }
749
+  
750
+  return(subtrees)
751
+}
752
+
753
+
754
+getRoot.df <- function(df, node){
755
+  root <- which(is.na(df$parent))
756
+  return(root)
757
+}
758
+
759
+##' Get parent node id of child node.
760
+##'
761
+##' @title getParent.df
762
+##' @param df tree data.frame
763
+##' @param node is the node id of child in tree.
764
+##' @return integer node id of parent
220 765
 getParent.df <- function(df, node) {
221 766
     i <- which(df$node == node)
222
-    res <- df$parent[i]
223
-    if (res == node) {
767
+    parent_id <- df$parent[i]
768
+    if (parent_id == node | is.na(parent_id)) {
224 769
         ## root node
225 770
         return(0)
226 771
     }
227
-    return(res)
772
+    return(parent_id)
228 773
 }
229 774
 
230 775
 getAncestor.df <- function(df, node) {
231 776
     anc <- getParent.df(df, node)
232 777
     anc <- anc[anc != 0]
233 778
     if (length(anc) == 0) {
234
-        stop("selected node is root...")
779
+        # stop("selected node is root...")
780
+      return(0)
235 781
     }
236 782
     i <- 1
237 783
     while(i<= length(anc)) {
... ...
@@ -243,6 +789,12 @@ getAncestor.df <- function(df, node) {
243 789
 }
244 790
 
245 791
 
792
+##' Get list of child node id numbers of parent node
793
+##'
794
+##' @title getChild.df
795
+##' @param df tree data.frame
796
+##' @param node is the node id of child in tree.
797
+##' @return list of child node ids of parent
246 798
 getChild.df <- function(df, node) {
247 799
     i <- which(df$parent == node)
248 800
     if (length(i) == 0) {
... ...
@@ -257,7 +809,8 @@ get.offspring.df <- function(df, node) {
257 809
     sp <- getChild.df(df, node)
258 810
     sp <- sp[sp != 0]
259 811
     if (length(sp) == 0) {
260
-        stop("input node is a tip...")
812
+        #stop("input node is a tip...")
813
+      return(0)
261 814
     }
262 815
 
263 816
     i <- 1
... ...
@@ -305,7 +858,6 @@ get.offspring.tip <- function(tr, node) {
305 858
 ##     N <- Ntip + Nnode
306 859
 ##     return(N)
307 860
 ## }
308
-
309 861
 getParent <- function(tr, node) {
310 862
     if ( node == getRoot(tr) )
311 863
         return(0)
... ...
@@ -323,7 +875,9 @@ getParent <- function(tr, node) {
323 875
 }
324 876
 
325 877
 getChild <- function(tr, node) {
878
+    # Get edge matrix from phylo object.
326 879
     edge <- tr[["edge"]]
880
+    # Select all rows that match "node".
327 881
     res <- edge[edge[,1] == node, 2]
328 882
     ## if (length(res) == 0) {
329 883
     ##     ## is a tip
... ...
@@ -363,6 +917,16 @@ isRoot <- function(tr, node) {
363 917
     getRoot(tr) == node
364 918
 }
365 919
 
920
+isTip <- function(tr, node) {
921
+  children_ids <- getChild(tr, node)
922
+  length(children_ids) == 0
923
+}
924
+
925
+isTip.df <- function(df, node) {
926
+  return(df[node, 'isTip'])
927
+}
928
+
929
+
366 930
 getNodeName <- function(tr) {
367 931
     if (is.null(tr$node.label)) {
368 932
         n <- length(tr$tip.label)
... ...
@@ -400,7 +964,6 @@ getNodeName <- function(tr) {
400 964
 ##     }
401 965
 ##     return(root)
402 966
 ## }
403
-
404 967
 get.trunk <- function(tr) {
405 968
     root <- getRoot(tr)
406 969
     path_length <- sapply(1:(root-1), function(x) get.path_length(tr, root, x))
... ...
@@ -459,10 +1022,54 @@ get.path_length <- function(phylo, from, to, weight=NULL) {
459 1022
 }
460 1023
 
461 1024
 getNodes_by_postorder <- function(tree) {
462
-    tree <- reorder.phylo(tree, "postorder")
1025
+  tree <- reorder.phylo(tree, "postorder")
463 1026
     unique(rev(as.vector(t(tree$edge[,c(2,1)]))))
464 1027
 }
465 1028
 
1029
+#getNodes_by_postorder.df <- function(df) {
1030
+    #tree <- reorder.phylo(tree, "postorder")
1031
+    #unique(rev(as.vector(t(tree$edge[,c(2,1)]))))
1032
+#}
1033
+
1034
+##' Get the nodes of tree from root in breadth-first order.
1035
+##' 
1036
+##' @title getNodesBreadthFirst.df
1037
+##' @param df tree data.frame
1038
+##' @return list of node id's in breadth-first order.
1039
+getNodesBreadthFirst.df <- function(df){
1040
+
1041
+  root <- getRoot.df(df)
1042
+  if(isTip.df(df, root)){
1043
+    return(root)
1044
+  }
1045
+
1046
+  tree_size <- nrow(df)
1047
+  # initialise list of nodes
1048
+  res <- root
1049
+  
1050
+  i <- 1
1051
+  while(length(res) < tree_size){
1052
+    parent <- res[i]
1053
+    i <- i + 1
1054
+    
1055
+    # Skip if parent is a tip.
1056
+    if(isTip.df(df, parent)){
1057
+      next
1058
+    }
1059
+    
1060
+    # get children of current parent.
1061
+    children <- getChild.df(df,parent)
1062
+
1063
+    # add children to result
1064
+    res <- c(res, children)
1065
+    
1066
+  }
1067
+  
1068
+  return(res)
1069
+  
1070
+}
1071
+
1072
+
466 1073
 getXcoord2 <- function(x, root, parent, child, len, start=0, rev=FALSE) {
467 1074
     x[root] <- start
468 1075
     x[-root] <- NA  ## only root is set to start, by default 0
... ...
@@ -819,6 +1426,7 @@ add_angle_slanted <- function(res) {
819 1426
     theta <- atan(dy/dx)
820 1427
     theta[is.na(theta)] <- 0 ## root node
821 1428
     res$angle <- theta/pi * 180
1429
+    
822 1430
     branch.y <- (res[res$parent, "y"] + res[, "y"])/2
823 1431
     idx <- is.na(branch.y)
824 1432
     branch.y[idx] <- res[idx, "y"]
... ...
@@ -929,8 +1537,6 @@ set_branch_length <- function(tree_object, branch.length) {
929 1537
 
930 1538
 ##     return(phylo)
931 1539
 ## }
932
-
933
-
934 1540
 re_assign_ycoord_df <- function(df, currentNode) {
935 1541
     while(anyNA(df$y)) {
936 1542
         pNode <- with(df, parent[match(currentNode, node)]) %>% unique
... ...
@@ -959,7 +1565,6 @@ re_assign_ycoord_df <- function(df, currentNode) {
959 1565
 ## is.ggtree <- function(x) inherits(x, 'ggtree')
960 1566
 
961 1567
 
962
-
963 1568
 calculate_angle <- function(data) {
964 1569
     data$angle <- 360/(diff(range(data$y)) + 1) * data$y
965 1570
     return(data)