### Know thy roots

Root system via dynamic L-system

A significantly dumbed-down 2D version of RootBox

Leitner et al (2010) The algorithmic beauty of plant roots – an L-System model for dynamic root growth simulation doi.org/10.1080/13873954.2010.491360

Leitner et al (2010) A dynamic root system growth model based on L-Systems doi.org/10.1007/s11104-010-0284-7

RootBox csc.univie.ac.at/rootbox/

```Canvas.setpenopacity(-1);

const top_h = -60;

const t = new Turtle();

function draw_dashed_line(y, Ndash) {
const dash = 200 / (2 * Ndash - 1)

t.jump(-100, y)

for (let i = 0; i < Ndash; i++) {
t.forward(dash)
t.penup()
t.forward(dash)
t.pendown()
}
}

draw_dashed_line(top_h, 100)

function random_normal(mu, sigma) {
let u = Math.random(), v = Math.random()
let z = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v) // Box–Muller transform

//z = Math.min(Math.max(z, -3.0), 3.0) // clip to 3 sigma

return mu + sigma * z
}

// function check_block(draw_step) { // sphere
//     let cy = -30

//     if (t.x()**2 + (t.y() - cy) **2 < 20**2) {
//         t.back(draw_step)

//         let new_dir = Math.atan2(t.x(), -(t.y() - cy)) / Math.PI * 180
//         if (new_dir < 0) {
//             new_dir += 180
//         }
//         else if (new_dir > 180) {
//             new_dir -= 180
//         }

//     }
// }

let cf_y1 = top_h + 25, cf_y2 = cf_y1 + 10, cf_y3 = cf_y2 + 10
let cf_w = 15

// draw coffin
t.jump(-cf_w, cf_y1)
t.goto(cf_w, cf_y1)
t.goto(cf_w + (cf_y2 - cf_y1), cf_y2)
t.goto(cf_w + (cf_y2 - cf_y1) - (cf_y3 - cf_y2), cf_y3)
t.goto(-cf_w - (cf_y2 - cf_y1) + (cf_y3 - cf_y2), cf_y3)
t.goto(-cf_w + -(cf_y2 - cf_y1), cf_y2)
t.goto(-cf_w, cf_y1)

t.jump(-cf_w - (cf_y2 - cf_y1), cf_y2)
t.goto(cf_w + (cf_y2 - cf_y1), cf_y2)

function check_block(draw_step) { // coffin
if ((t.y() < cf_y1) || (t.y() > cf_y3)) {
return
}
else if ((t.y() < cf_y2) && (Math.abs(t.x()) > cf_w + (t.y() - cf_y1))) {
return
}
else if ((t.y() > cf_y2) && (Math.abs(t.x()) > cf_w + (cf_y2 - cf_y1) - (t.y() - cf_y2))) {
return
}
else {
t.back(draw_step)

if (t.y() < cf_y2) {
if (t.x() < -cf_w) {
}
else if (t.x() < 0) {
}
else if (t.x() < cf_w) {
}
else {
}
}
else {
}
}
}

function draw_root(x0, y0, angle0,
max_angle,
l_base,
l_end,
l_mid, l_mid_sd,
n_branches,
N_try_grav, try_grav_depth,
angle_sigma,
draw_step,
br_theta, br_theta_sd, br_l, br_l_sd) {
let L_max = l_base + l_end

let L_mid = []
for (let i = 0; i < n_branches; i++) {
Lm = random_normal(l_mid, l_mid_sd)

if (i == 0) {
L_mid[0] = l_base + Lm
}
else {
L_mid[i] = L_mid[i - 1] + Lm
}

L_max += Lm
}

t.jump(x0, y0)

let L = 0.0

let n_steps = 0

let n_br = 0

while (L < L_max) {
t.forward(draw_step)

L += draw_step

if (t.y() < y0) {
t.sety(y0)
}

check_block(draw_step)

if ((n_br < n_branches) && (L > L_mid[n_br])) {
let mem_x = t.x(), mem_y = t.y(), mem_h = t.heading()

let dtheta = random_normal(br_theta, br_theta_sd)
let angle_br = (Math.random() < 0.5) ? (mem_h + dtheta) : (mem_h - dtheta)
let L_br = random_normal(br_l, br_l_sd)

draw_root(mem_x, mem_y, angle_br, 360, L_br, 0, 0, 0, 0, 1, 0, 18, draw_step, 0, 0, 0, 0)

t.jump(mem_x, mem_y)

n_br++
}

let N = 1
if ((t.y() - y0) > try_grav_depth) {
N = N_try_grav
}
else if (Math.random() <= 0.3) {
N = Math.floor(1 + N_try_grav * Math.random())
}

let best_angle = -90

for (let i = 0; i < N; i++) {
let new_angle = t.heading() + random_normal(0, Math.sqrt(draw_step) * angle_sigma)

if (Math.abs(new_angle - 90) < Math.abs(best_angle - 90)) {
best_angle = new_angle
}
}

best_angle = Math.min(Math.max(best_angle, 90 - max_angle), 90 + max_angle)

}
}

const R = 3

t.jump(-R, top_h)
t.goto(-R, -100)

t.jump(R, top_h)
t.goto(R, -100)

const d = 2.0, Nd = 60

for (let i = 0; i < Nd; i++) {
let y = -100 + (top_h + 100) * Math.random()
let dd = d * Math.random()
let x = (Math.random() <= 0.25) ? -R : ((Math.random() <= 0.33) ? (R - dd) : -R + (2 * R - dd) * Math.random())

t.jump(x, y)
t.goto(x + dd, y)
}

const s = 1.5
const N0 = 30
const mod = 0.5

for (let i = 0; i < N0; i++) {
let angle = 180 / (N0 + 2) * (1 + i + Math.random() - 0.5)
let x = (R + 1) * (-1 + 2 * Math.random())

draw_root(x, top_h + 0.5, angle, 110, 0.1 * s, 18 * s, 0.6 * s / mod, 0.06 * s / mod, 120 * mod, 2, 30 * s, 9, 0.25, 70, 15, 1.5 * s, 0.5 * s)
}```