Skip to content

Instantly share code, notes, and snippets.

@AdamSpannbauer
Created July 24, 2018 17:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AdamSpannbauer/284f2376a5458557d509e2aefe6096c6 to your computer and use it in GitHub Desktop.
Save AdamSpannbauer/284f2376a5458557d509e2aefe6096c6 to your computer and use it in GitHub Desktop.
Create/Draw a Binary Fractal Tree with plotly in R
# Create/Draw a Binary Fractal Tree
library(plotly)
# basic strucure for line shape in plotly
base_line = list(
type = "line",
line = list(color = "black"),
xref = "x",
yref = "y",
x0 = NA,
x1 = NA,
y0 = NA,
y1 = NA
)
# create a plotly line shape given start and end coords
create_line = function(p1, p2) {
base_line[['x0']] = p1[1]
base_line[['y0']] = p1[2]
base_line[['x1']] = p2[1]
base_line[['y1']] = p2[2]
return(base_line)
}
# create line segment from (0, 0) to (0, len) to be trunk of fractal tree
create_trunk = function(len = 1) {
l = create_line(c(0, 0), c(0, len))
return(list(points = c(0, len),
branches = list(l)))
}
# creates end point of line segment to satisfy length and
# angle inputs from given start coord
gen_end_point = function(xy, len = 5, theta = 45) {
dy = sin(theta) * len
dx = cos(theta) * len
newx = xy[1] + dx
newy = xy[2] + dy
return(c(newx, newy))
}
# create a single branch of fractal tree
# returns branch endpoint coords and a plotly line shape to represent branch
branch = function(xy, angle_in, delta_angle, len) {
end_point = gen_end_point(xy, len = len, theta = angle_in + delta_angle)
line_shape = create_line(xy, end_point)
return(list(points=end_point, branches=list(line_shape)))
}
# helper function to aggregate branch objects into single branch object
collect_branches = function(branch1, branch2) {
collected_points = rbind(branch1$points, branch2$points)
collected_branches = c(branch1$branches, branch2$branches)
return(list(points=collected_points, branches=collected_branches))
}
# recursively create fractal tree branches
create_branches = function(xy,
angle_in = pi / 2,
delta_angle = pi / 8,
len = 1,
min_len = 0.01,
len_decay = 0.2) {
if (len < min_len) {
return(NULL)
} else {
branch_left = branch(xy, angle_in, delta_angle, len)
subranches_left = create_branches(branch_left$points,
angle_in = angle_in + delta_angle,
delta_angle = delta_angle,
len = len * len_decay,
min_len = min_len,
len_decay = len_decay)
branches_left = collect_branches(branch_left, subranches_left)
branch_right = branch(xy, angle_in, -delta_angle, len)
subranches_right = create_branches(branch_right$points,
angle_in = angle_in - delta_angle,
delta_angle = delta_angle,
len = len * len_decay,
min_len = min_len,
len_decay = len_decay)
branches_right = collect_branches(branch_right, subranches_right)
return(collect_branches(branches_left, branches_right))
}
}
# create and draw binary fractal tree
draw_fractal_tree = function(trunk_len=10,
delta_angle = pi / 8,
len_decay=0.7,
min_len=0.25) {
trunk = create_trunk(trunk_len)
branches = create_branches(trunk$points,
delta_angle =delta_angle,
len = trunk_len * len_decay,
min_len = min_len,
len_decay = len_decay)
tree = collect_branches(trunk, branches)
tree_points_df = as.data.frame(tree$points)
names(tree_points_df) = c('x', 'y')
null_axis = list(
title = '',
zeroline = FALSE,
showline = FALSE,
showticklabels = FALSE,
showgrid = FALSE
)
plot_ly(
tree_points_df,
x = ~ x,
y = ~ y,
type = 'scatter',
mode = 'markers',
color = ~ x,
colors = 'Spectral',
hoverinfo = "none",
showlegend = FALSE
) %>%
layout(shapes = tree$branches,
xaxis = null_axis,
yaxis = null_axis) %>%
hide_colorbar() %>%
config(displayModeBar = FALSE)
}
# example usage/output
draw_fractal_tree(delta_angle = pi / 2)
draw_fractal_tree(delta_angle = pi / 4)
draw_fractal_tree(delta_angle = pi / 8)
draw_fractal_tree(delta_angle = pi / 16)
@AdamSpannbauer
Copy link
Author

Example output

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment