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
#> [1] "1[-1[-1[-1[-1[-0]+0]+1[-0]+0]+1[-1[-0]+0]+1[-0]+0]+1[-1[-1[-0]+0]+1[-0]+0]+1[-1[-0]+0]+1[-0]+0]+1[-1[-1[-1[-0]+0]+1[-0]+0]+1[-1[-0]+0]+1[-0]+0]+1[-1[-1[-0]+0]+1[-0]+0]+1[-1[-0]+0]+1[-0]+0"Build the tree data.frame from the string, and visualize the output.
tree <- build_tree(string = tree_string, angle = 15)
head(tree)
#> # A tibble: 6 × 5
#> from_x to_x from_y to_y type
#> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 0 1.04e-17 0 0.170 branch
#> 2 1.04e-17 4.39e- 2 0.170 0.333 branch
#> 3 4.39e- 2 1.29e- 1 0.333 0.480 branch
#> 4 1.29e- 1 2.49e- 1 0.480 0.600 branch
#> 5 2.49e- 1 3.95e- 1 0.600 0.685 branch
#> 6 3.95e- 1 5.59e- 1 0.685 0.729 leafVisualize 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 + tree3The 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.
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 + tree3The 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 + p3plot_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 + tree3Colors 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')