Generate the data specifying a flametree

flametree_grow(
  seed = 286,
  time = 6,
  scale = c(0.6, 0.8, 0.9),
  angle = c(-10, 10, 20),
  split = 2,
  trees = 1,
  seg_col = spark_linear(tree = 2, time = 1),
  seg_wid = spark_decay(time = 0.3, multiplier = 5, constant = 0.1),
  shift_x = spark_random(multiplier = 3),
  shift_y = spark_nothing()
)

Arguments

seed

Integer seed for the random number generator

time

Number of generations to run the iterative process

scale

Vector of possible values for the "size rescaling" at each iteration

angle

Vector of possible angle changes (in degrees) at each iteration

split

Number of splits at each time point

trees

Number of trees to generate

seg_col

Spark function to control the segment colour

seg_wid

Spark function to control the segment width

shift_x

Spark function to control horizontal jitter

shift_y

Spark function to control vertical jitter

Value

The output of flametree_grow()` is a tibble with the following columns: coord_x, coord_y, id_tree, id_time, id_path, id_leaf, id_pathtree, id_step, seg_deg, seg_len, seg_col, and seg_wid. Each row in the tibble specifies a single point: every curved segment is defined by three such rows.

The two "coord" columns are numeric variables that specify the location of the point itself. The "id" columns are used as indicators of various kinds. The id_tree column contains numbers specifying which tree each point belongs to, and similarly the id_time column is a numeric identifier that specifies the time point at which the point was generated (i.e., the iteration of the generative process). The id_step column contains a number (0, 1, or 2) indicating whether the point is the first point, the midpoint, or the end point of the relevant curved segment in a tree. In addition, there are two identifier columns used to denote the segments themselves. The id_path column is numeric, and assigns value 1 to the "first" segment (i.e., the lowest part of the tree trunk) for every tree, with values increasing numerically for each subsequent segment. Values for id_path will uniquely identify a segment within a tree, but when multiple trees are generated there will be multiple segments that have the same id_path value. If a unique identifier across trees is needed, use the id_pathtree column, which is a character vector constructed by pasting the id_path and id_tree values into a string, with an underscore as the separator character.

In addition to the two coordinate columns and the six identifier columns, the data generated by flametree_grow() contains four "seg" columns that are intended to map onto different visual characteristics of a plot. The seg_deg column specifies the orientation of the segment, whereas seg_len denotes the length of the segment, seg_col specifies the colour (as a numeric value that could be interpreted by a palette), and seg_wid specifies the width of the segment. Note that this information use used differently by the flametree_plot() function, depending on what style of plot is generated.

Details

Generative art created with flametree is a visualisation of a data structure created by calling flametree_grow(). The underlying algorithm is an iterative branching process: each tree starts out as a single vertical segment, to which multiple new segments are added at the end of the first iteration. Over multiple iterations this creates a tree-like structure.

The user can control how this iterative process unfolds. By setting the seed argument the random number generator is reset using set.seed(). The trees argument specifies the number of trees to create using this process, the time argument specifies how many iterations of the branching process will be run (at least two), and the split argument specifies how many new segments (at least two) will be created each time abranching occurs.

When a new segment is created, its size and orientation are controlled by the scale and angle arguments. The scale argument takes a vector of at least two positive numbers. One of these numbers is selected at random whenever a new segment is created, and the length of the new segment is equal to the length of the "parent" segment from which it was created, multiplied by this scaling factor. The orientation of the new segment is controlled by the angle argument in an analogous way. Every time a new segment is generated, one of these angles (interpreted in degrees, not radians) is selected at random. The orientation of the new segment is equal to the orientation of the parent segment plus the sampled angle. Like the scale argument, angle must contain at least two values.

The remaining arguments (seg_col, seg_wid, shift_x, and shift_y) all take functions as their input, and are used to control how the colours (seg_col) and width (seg_wid) of the segments are created, as well as the horizontal (shift_x) and vertical (shift_y) displacement of the trees are generated. Functions passed to these arguments take four inputs: coord_x, coord_y, id_tree, and id_time. Any function that takes these variables as input can be used for this purpose. However, as a convenience, four "spark" functions are provided that can be used to create functions that are suitable for this purpose: spark_linear(), spark_decay(), spark_random(), and spark_nothing().

These functions are documented in their own help files. To give an example, the default behaviour of flametree_grow() adds a random horizontal displacement to each tree to give the impression of multiple trees growing side by side. To suppress this horizontal displacement, set shift_x = spark_nothing().

Examples

# flametree data structure with default parameters
flametree_grow()
#> # A tibble: 381 × 12
#>    coord_x coord_y id_tree id_time id_path id_leaf id_pathtree id_step seg_deg
#>      <dbl>   <dbl>   <int>   <int>   <int> <lgl>   <chr>         <int>   <dbl>
#>  1   -1.11    0          1       1       1 FALSE   1_1               0      90
#>  2   -1.11    0.5        1       1       1 FALSE   1_1               1      90
#>  3   -1.11    1          1       1       1 FALSE   1_1               2      90
#>  4   -1.11    1          1       2       2 FALSE   1_2               0      80
#>  5   -1.11    1.3        1       2       2 FALSE   1_2               1      80
#>  6   -1.00    1.59       1       2       2 FALSE   1_2               2      80
#>  7   -1.11    1          1       2       3 FALSE   1_3               0     100
#>  8   -1.11    1.45       1       2       3 FALSE   1_3               1     100
#>  9   -1.26    1.89       1       2       3 FALSE   1_3               2     100
#> 10   -1.00    1.59       1       3       4 FALSE   1_4               0      90
#> # … with 371 more rows, and 3 more variables: seg_len <dbl>, seg_col <dbl>,
#> #   seg_wid <dbl>

# setting time = 10 runs the generative process
# longer resulting in a table with more rows
flametree_grow(time = 10)
#> # A tibble: 6,141 × 12
#>    coord_x coord_y id_tree id_time id_path id_leaf id_pathtree id_step seg_deg
#>      <dbl>   <dbl>   <int>   <int>   <int> <lgl>   <chr>         <int>   <dbl>
#>  1    1.22    0          1       1       1 FALSE   1_1               0      90
#>  2    1.22    0.5        1       1       1 FALSE   1_1               1      90
#>  3    1.22    1          1       1       1 FALSE   1_1               2      90
#>  4    1.22    1          1       2       2 FALSE   1_2               0      80
#>  5    1.22    1.3        1       2       2 FALSE   1_2               1      80
#>  6    1.33    1.59       1       2       2 FALSE   1_2               2      80
#>  7    1.22    1          1       2       3 FALSE   1_3               0     100
#>  8    1.22    1.45       1       2       3 FALSE   1_3               1     100
#>  9    1.07    1.89       1       2       3 FALSE   1_3               2     100
#> 10    1.33    1.59       1       3       4 FALSE   1_4               0      90
#> # … with 6,131 more rows, and 3 more variables: seg_len <dbl>, seg_col <dbl>,
#> #   seg_wid <dbl>

# default behaviour is to randomly displace trees
# by random horizontal perturbation: to switch this
# off use the spark_nothing() function
flametree_grow(shift_x = spark_nothing())
#> # A tibble: 381 × 12
#>      coord_x coord_y id_tree id_time id_path id_leaf id_pathtree id_step seg_deg
#>        <dbl>   <dbl>   <int>   <int>   <int> <lgl>   <chr>         <int>   <dbl>
#>  1  0           0          1       1       1 FALSE   1_1               0      90
#>  2  0           0.5        1       1       1 FALSE   1_1               1      90
#>  3  0           1          1       1       1 FALSE   1_1               2      90
#>  4  0           1          1       2       2 FALSE   1_2               0      80
#>  5  1.84e-17    1.3        1       2       2 FALSE   1_2               1      80
#>  6  1.04e- 1    1.59       1       2       2 FALSE   1_2               2      80
#>  7  0           1          1       2       3 FALSE   1_3               0     100
#>  8  2.76e-17    1.45       1       2       3 FALSE   1_3               1     100
#>  9 -1.56e- 1    1.89       1       2       3 FALSE   1_3               2     100
#> 10  1.04e- 1    1.59       1       3       4 FALSE   1_4               0      90
#> # … with 371 more rows, and 3 more variables: seg_len <dbl>, seg_col <dbl>,
#> #   seg_wid <dbl>