knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(fractalforest)
This vignette presents some features of the fractalforest
package for customizing fractal trees.
Install developer version.
# install.packages('devtools') library(devtools) install_github('sergiocostafh/fractalforest')
Load fractalforest package.
library(fractalforest)
The fractalforest package uses the Lindenmayer system (L-system) to generate fractal trees. By applying recursive production rules, the package simulates natural branching patterns, allowing for the creation of realistic and customizable tree structures.
We will start by defining the L-system rules for constructing a simple binary tree, and then we will go into the details.
binary_tree_rules <- data.frame(inp = c("0", "1"), out = c("1[-0]+0", "1"), stringsAsFactors = FALSE)
Use the iterate_lsystem
function to build the string based on the rules (rules
), the axiom (init
) and the number of iterations (n
).
tree_string <- iterate_lsystem(init = "0", rules = binary_tree_rules, n = 5) tree_string
Build the tree data.frame from the string, and visualize the output.
tree <- build_tree(string = tree_string, angle = 15) head(tree)
Visualize the tree.
plot_tree(tree)
To set the rules, the following characters are recognized:
"+" Turn by positive angle.
"-" Turn by negative angle.
"[" Save current position and heading.
"]" Restore saved position and heading (allows one to go back).
")" Reduces length and diameters of branches.
"(" Increment length and diameters of branches.
Any other characters in the rule string will be recognized as "a move forward, drawing as you go" instruction. This means that they must be declared in the inp
column of the rules data frame, and also its corresponding substitution rule (out
column).
The following example demonstrates the use of parentheses to reduce/increase branch lengths.
binary_tree_rules <- data.frame(inp = c("0", "1"), out = c("1[-(0)]+(0)", "1"), stringsAsFactors = FALSE) tree_string <- iterate_lsystem(init = "0", rules = binary_tree_rules, n = 5) tree <- build_tree(string = tree_string, angle = 15) plot_tree(tree)
iterate_lsystem
functionThe n
argument controls the number of iterations do build the plant. A large n
value can be time and memory consuming, so be careful.
library(ggplot2) library(patchwork) library(dplyr) trees_n <- lapply(1:8, function(x) { iterate_lsystem(init = '0', rules = binary_tree_rules, n = x) %>% build_tree(string = ., angle = 15) %>% plot_tree()+ labs(title = paste0('n = ', x)) }) wrap_plots(trees_n, nrow = 2, heights = c(1,1))
Below are some other rules for simulating plants with different morphologies.
alternate_bush_rules <- data.frame(inp = c("0", "1"), out = c("1[+1][--(0)]+1-1[+++(0)]-(0)", "1"), stringsAsFactors = FALSE) tree_string <- iterate_lsystem(init = "0", rules = alternate_bush_rules, n = 6) tree1 <- build_tree(string = tree_string, angle = 10) %>% plot_tree()+ labs(title = 'alternate tree') arrow_weed_rules <- data.frame(inp = c("0", "1"), out = c("1[+(0)][-(0)](10)", "1"), stringsAsFactors = FALSE) tree_string <- iterate_lsystem(init = "0", rules = arrow_weed_rules, n = 6) tree2 <- build_tree(string = tree_string, angle = 30) %>% plot_tree()+ labs(title = 'arrow weed') twiggy_weed_rules <- data.frame(inp = c("0", "1"), out = c("1[-(0)]1[-(0)]+(0)", "1"), stringsAsFactors = FALSE) tree_string <- iterate_lsystem(init = "0", rules = twiggy_weed_rules, n = 6) tree3 <- build_tree(string = tree_string, angle = 25) %>% plot_tree()+ labs(title = 'twiggy weed') tree1 + tree2 + tree3
The fractalforest package has some predefined models for constructing fractal trees from L-system rules. These models can be accessed from the fractal_tree_model
function. The function takes a mandatory argument (tree_model
) and an optional one (n_iter
) that controls the number of iterations for string construction.
There are 10 pre-implemented tree templates, as follows (default n_iter
between parentheses):
-1 or "binary_tree" (6);
-2 or "alternate_tree" (5);
-3 or "arrow_weed" (5);
-4 or "twiggy_weed" (5);
-5 or "stochastic_fuzzy_weed" (4);
-6 or "crooked_binary_tree" (6);
-7 or "crooked_alternate_tree" (5);
-8 or "crooked_arrow_weed" (5);
-9 or "crooked_twiggy_weed" (5);
-10 or "crooked_stochastic_fuzzy_weed" (4).
fractal_tree_model(5) %>% build_tree() %>% plot_tree()
The crooked models add random tortuosity to the tree models. For this to take effect, it is necessary to set the randomness = TRUE
argument in the build_tree
function.
All predefined tree models are presented below.
for(i in 1:10){ if(i > 5){rdns = TRUE} else{rdns = FALSE} t <- fractal_tree_model(i) %>% build_tree(randomness = rdns) %>% mutate(model = i) if(i == 1){ ts <- t } else{ ts <- bind_rows(ts, t) } } ts <- ts %>% mutate(model = paste0('model = ',model)) ts <- ts %>% mutate(model = factor(model, levels = unique(ts$model), ordered = T)) library(sf) df_geom <- ts[1:4] %>% dplyr::mutate(ID = dplyr::row_number()) %>% dplyr::select(5,1,3,2,4) rows <- df_geom %>% split(., seq(nrow(.))) lines <- lapply(rows, function(row) { lmat <- matrix(unlist(row[2:5]), ncol = 2, byrow = TRUE) sf::st_linestring(lmat) }) lines <- sf::st_sfc(lines) lines_sf <- sf::st_sf('ID' = df_geom$ID, 'geometry' = lines) plot_df <- ts %>% dplyr::mutate(geometry = sf::st_buffer(lines_sf, dist = .00001, endCapStyle = 'SQUARE') %>% dplyr::pull(geometry)) %>% sf::st_as_sf() %>% dplyr::group_by(type, model) %>% dplyr::summarise() ggplot2::ggplot()+ ggplot2::geom_sf(data = plot_df)+ ggplot2::theme_void()+ facet_wrap(~model, nrow = 2)
build_tree
functionThe purpose of this function is to build a data frame that contains the coordinate matrix and some other information about the plant, such as the differentiation between branches and leaves and the diameters along the branches.
The h_reduction
parameter in the build_tree
function, controls the reduction factor of the length.
tree1 <- build_tree(string = tree_string, angle = 15, h_reduction = .7) %>% plot_tree()+ labs(title = 'h_reduction = .7') tree2 <- build_tree(string = tree_string, angle = 15) %>% plot_tree()+ labs(title = 'h_reduction = .61803 (default)') tree3 <- build_tree(string = tree_string, angle = 15, h_reduction = .5) %>% plot_tree()+ labs(title = 'h_reduction = .5') tree1 + tree2 + tree3
The randomness
argument, as the name suggests, allows one to apply randomness to the angles and lengths of the plant. It must be set to TRUE
if its effects are desired The amount of randomness can be controlled by the angle_cv
and length_cv
arguments, which define the angle and length variation coefficients, respectively. By default, these two arguments are set to 0.1,
tree1 <- build_tree(string = tree_string, angle = 15, randomness = TRUE) %>% plot_tree()+ labs(title = 'length_cv = .1 (default)') tree2 <- build_tree(string = tree_string, angle = 15, randomness = TRUE, length_cv = .5) %>% plot_tree()+ labs(title = 'length_cv = .5') tree3 <- build_tree(string = tree_string, angle = 15, randomness = TRUE) %>% plot_tree()+ labs(title = 'angle_cv = .1 (default)') tree4 <- build_tree(string = tree_string, angle = 15, randomness = TRUE, angle_cv = .5) %>% plot_tree()+ labs(title = 'angle_cv = .5') (tree1 + tree2) / (tree3 + tree4)
It's possible to set the plant height setting the height
parameter in the build_tree
function. It will affect the resulting coordinates in the output.
To visualize the y axis with the height measure, ggplot2
functions can be used together with plot_tree
function.
tree <- build_tree(string = tree_string, angle = 15, height = 10) plot_tree(tree)+ labs(x = '', y = 'Height (m)')+ theme_bw()
As well as the height
, in the build_tree
function it is possible to define the diameter
of branches, whose reduction is defined by the parentheses in the rules declared in the iterate_lsystem
function, in the same way as lengths. The argument d_reduction
controls the reduction factor for the diameters.
The density of leaves in the tree crown can be customized by the leaf_size
argument.
p1 <- build_tree(string = tree_string, angle = 15, height = 10, diameter = 20) %>% plot_tree(d_col = diameter)+ labs(title = 'leaf_size = NULL (default)') p2 <- build_tree(string = tree_string, angle = 15, height = 10, diameter = 20, leaf_size = 10) %>% plot_tree(d_col = diameter,)+ labs(title = 'leaf_size = 10') p3 <- build_tree(string = tree_string, angle = 15, height = 10, diameter = 20, leaf_size = 20) %>% plot_tree(d_col = diameter)+ labs(title = 'leaf_size = 20') p1 + p2 + p3
plot_tree
functionThis function was implemented on top of ggplot2
and for this reason it can be used together with several functions of the package.
To visualize the diameters along the plant, the diameter column must be declared in the d_col
argument.
tree1 <- build_tree(string = tree_string, angle = 15, height = 5, diameter = 7, d_reduction = .7) %>% plot_tree(d_col = diameter)+ labs(title = 'd_reduction = .7') tree2 <- build_tree(string = tree_string, angle = 15, height = 5, diameter = 7) %>% plot_tree(d_col = diameter)+ labs(title = 'd_reduction = .61803 (default)') tree3 <- build_tree(string = tree_string, angle = 15, height = 5, diameter = 7, d_reduction = .5) %>% plot_tree(d_col = diameter)+ labs(title = 'd_reduction = .5') tree1 + tree2 + tree3
Colors can be used to differentiate leaves and branches, by declaring the leaf_color
and branch_color
arguments. The build_tree
function identifies as leaves all the branches positioned at the ends of the plant. This information is stored in the "type" column, which can be used to assign colors.
library(patchwork) tree_string <- iterate_lsystem(init = "0", rules = binary_tree_rules, n = 10) tree <- build_tree(string = tree_string, angle = 15, height = 10, diameter = 20) plot_tree(tree, d_col = diameter, branch_color = 'lightsalmon4', leaf_color = 'darkgreen')
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.