Sean Gilligan
Fractals are mathematical objects made from simple recursive processes that produce potentially infinite identical patterns across many different scales. Many fractals appear in nature. The website The Science Explorer provides some examples which I have reproduced below. The package I’ve written allows for the production of fractal trees, both deterministic and with some added randomness in a couple domains. It also allows for gif production to make trees that sway in the wind.
devtools::load_all()
basic_deterministic_trees(splits = 3, length = 2, scale_length = T, length_scale = 1.4, children = 2,
start_angle = 0, angle = pi/(splits/2 + 1), scale_angle = T, angle_scale = sqrt(1.272018), thickness = 2,
scale_thickness = T, thickness_scale = 1.61803, taper = T, man_lengths = 0, man_angles = 0,
man_split_thickness = 0, man_begin_thick = 0, man_end_thick = 0, man_children = 0, sib_ratio = 0,
title = NA, plot = T, datadump = F)
splits : (int) Indicates number of levels beyond starting branch.
length : (dbl) Indicates length of a branch.
scale_length : (lgl) Indicates if lengths should be scaled at each new level.
length_scale : (dbl) Indicates rate in which branch lengths shorten at each level.
children : (int) Indicates number of new branches at each new level.
start_angle : (dbl) Indicates angle in radians of starting branch, measured ccw from
the +y direction.
angle : (dbl) Indicates angle in radian between each branch at a split.
scale_angle : (lgl) Indicates if branch split angles should be scaled at each new level.
angle_scale : (lgl) Indicates rate at which angles should decrease
thickness : (dbl) Indicates thickness of a branch.
scale_thickness : (lgl) Indicates if thickness should should shrink at each new level
thickness_scale : (dbl) Indicates rate in which branch thicknesses should shrink
taper : (lgl) Indicates if branches should taper.
man_lengths : Manually select branch length for starting branch and branches at each level.
man_angles : Manually select angles between branches at each split by level.
man_split_thickness : Manually select thickness of each branch at each split in order.
man_begin_thick : Manually select starting thickness by level.
man_end_thick : Manually select ending thickness.
man_children : Manually select number of branches at split by level.
sib_lgth_ratio : Only works if equal number of children at each split. Vector indicates
relative size of children at each split.
title : (chr) Optional title for output tree.
plot : (lgl) Default to T for plotting
datadump : (lgl) Default to F. Set to T get relevant data.
par(mfrow=c(2,3), mar=c(1,1,1,1))
deterministic_tree(splits = 1, title = "splits = 1")
deterministic_tree(splits = 2, title = "splits = 2")
deterministic_tree(title = "default splits = 3")
deterministic_tree(splits = 4, title = "splits = 4")
deterministic_tree(splits = 5, title = "splits = 5")
deterministic_tree(splits = 6, title = "splits = 6")
par(mfrow=c(2,2), mar=c(1,1,1,1))
deterministic_tree(children = 1, title = "children = 1")
deterministic_tree(title = "default children = 2")
deterministic_tree(children = 3, title = "children = 3")
deterministic_tree(children = 4, title = "children = 4")
par(mfrow=c(2,2), mar=c(1,1,1,1))
deterministic_tree(man_children = c(2,3,4), title = "man_children = c(4,3,2)")
deterministic_tree(man_children = c(3,2,3), title = "man_children = c(3,2,3)")
# Having "man_children" length =/= "splits" changes "splits" to length.
deterministic_tree(man_children = 4, title = "man_children = 4")
deterministic_tree(man_children = c(3,3,3,3,3), title = "man_children = c(3,3,3,3,3)")
par(mfrow=c(2,3), mar=c(1,1,1,1))
deterministic_tree(splits = 5, title = "all = T")
deterministic_tree(splits = 5, taper = F, title = "scale_thickness = T, taper = F")
deterministic_tree(splits = 5, scale_thickness = F, taper = F, title = "scale_thickness = F, taper = F")
deterministic_tree(splits = 5, scale_length = F, title = "scale_length = F")
deterministic_tree(splits = 5, scale_angle = F, title = "scale_angle = F")
par(mfrow=c(2,3), mar=c(1,1,1,1))
deterministic_tree(splits = 8, sib_lgth_ratio = c(1,2), title = "splits = 8, sib_ratio = c(1,2)")
deterministic_tree(splits = 6, sib_lgth_ratio = c(1,2,1), title = "splits = 6, sib_ratio = c(1,2,1)")
deterministic_tree(splits = 5, sib_lgth_ratio = c(1,2,2,1), title = "splits = 5, sib_ratio = c(1,2,2,1)")
deterministic_tree(splits = 5, sib_lgth_ratio = c(2,1,1,2), title = "splits = 5, sib_ratio = c(1,2,2,1)")
deterministic_tree(splits = 6, sib_lgth_ratio = c(1,2,3), title = "splits = 6, sib_ratio = c(1,2,3)")
deterministic_tree(splits = 6, sib_lgth_ratio = c(2,1,2), title = "splits = 6, sib_ratio = c(1,2,3)")
Explore “trunk_scale”. It simply shrinks the starter branch. Other arguments: “splits = 6, angle_scale = 1.25, sib_lgth_ratio = c(1,5,1)”
par(mfrow=c(1,3), mar=c(1,1,1,1))
deterministic_tree(splits = 6, angle_scale = 1.25, sib_lgth_ratio = c(1,5,1), title = "default: trunk_scale = 1")
deterministic_tree(splits = 6, trunk_scale = 0.75, angle_scale = 1.25, sib_lgth_ratio = c(1,5,1), title = "trunk_scale = 0.75")
deterministic_tree(splits = 6, trunk_scale = 0.25, angle_scale = 1.25, sib_lgth_ratio = c(1,4,1), title = "trunk_scale = 0.25")
Chapter 9 of Daniel Shiffman’s book discusses fractals and random/deterministic trees. It was the main inspiration for this project, and part of my goal was to be able to casually reproduce some of the examples he provides. I do so below.
par(mfrow=c(1,3), mar=c(1,1,1,1))
deterministic_tree(splits = 8, children = 2, angle = 9*pi/12, scale_angle = F,
scale_thickness = F, taper = F, thickness = 0.5, length_scale = 1.5)
deterministic_tree(splits = 8, children = 2, angle = pi/4, scale_angle = F,
scale_thickness = F, taper = F, thickness = 0.5, length_scale = 1.5)
deterministic_tree(splits = 8, children = 2, angle = pi/20, scale_angle = F,
scale_thickness = F, taper = F, thickness = 0.5, length_scale = 1.5)
Essentially the same as deterministic_trees()
but adds a
few additional arguments.
random_angles : (lgl) Toggles angle noise on/off. Random values currently chosen
via sampling from normal distribution.
angle_variance : (dbl) Indicates base variance for angle noise. By default "set" to
zero be given value based on other inputs. In particular by (angles[2]/4)^2,
where angles[2] is the angle between branches at the first split.
random_lengths : (lgl) Toggles length noise on/off. Random values currently chosen via
sampling from normal distribution.
length_variance : (dbl) Indicates base variance for length noise. By default "set" to
zero be given value later based on other inputs. In particular by lengths[1]/24,
where lengths[1] is the length of the starting branch.
The effects look admittedly better for some tree specifications than others.
titles <- rep(c("both = F", "random_angles = T", "random_lengths = T", "both = T"), each = 4)
ras <- rep(c(F,F,F,F,T,T,T,T), 2)
rls <- c(rep(F,8),rep(T,8))
par(mfrow=c(4,4), mar=c(1,1,1,1))
for(i in 1:16){
random_tree(splits = 8, children = 2, angle = pi/4, scale_angle = F, length_scale = 1.4, random_angles = ras[i], random_lengths = rls[i], title = titles[i])
}
par(mfrow=c(4,4), mar=c(1,1,1,1))
for(i in 1:16){
random_tree(splits = 6, trunk_scale = 0.5, angle_scale = 1.25, sib_lgth_ratio = c(1,3,1), random_angles = ras[i], random_lengths = rls[i], title = titles[i])
}
Both functions above allow the user to store the output data of the tree
for potential later modification. The function
swaying_tree()
takes this data dump as an input with a
couple randomness variables to simulate wind patterns in the form of a
random field of to create a GIF of a “swaying” tree. The images for the
GIF file are stored locally before being deleted upon GIF formation.
fractal_tree <- random_tree(splits = 8, children = 2, angle = pi/4, scale_angle = F, random_angles = T,
random_lengths = T, length_scale = 1.4, plot = F, datadump = T)
This code lives in the function swaying_tree()
, with
exception of the plotting.
model <- RandomFields::RMexp(var = 0.02, scale = 0.4)
branch_count <- sum(cumprod(fractal_tree$fun_variables$children)) + 1
x <- seq(0, 10, length.out = 100)
y <- seq(0, 10, length.out = branch_count)
simu <- suppressMessages(as.matrix(RandomFields::RFsimulate(model, x, y, grid=TRUE)))
raster::plot(raster::raster(simu))
An example input and possible GIF.
filename <- swaying_tree(fractal_tree, var = 0.02, scale = 0.4, return_filename = T)