Create A Tree Generator (in less than 100 lines of V)

Subscribe to my newsletter and never miss my upcoming articles

A Magical Tree Generator

In this post, I'll show you how to do what you've always wanted. Namely a magical tree generator - and of course, learn V in the making of this.

This will all be done in exactly 90 lines of code.

V

First of all, what is V? This is easy, I'll just simply refer you to my other post: madco.me/code-in-v

Tree Generator

Let's get started!

As V is much like Go in it's code, we need a module and some imports

module main

import gg
import gx
import math
import rand
import sokol.sapp

These are just a few things V provides for us to use, and that we'll need.

  • gg is our drawing tool, think of it as the plain ol' plaint buddy from MS.
  • gx is the a plalette of colours for us to use.
  • math is for PI.
  • rand to gain some randomness. We do not want the same thing multiple times.
  • sokol.sapp, to be honest, I have yet to figure this.
const (
    win_width  = 600
    win_height = 600
)

This will be the size of our canvas.

struct App {
mut:
    gg    &gg.Context
}

This struct will be in charge of drawing.

struct Branch {
mut:
    x f32
    y f32
    cx f32
    cy f32
    rad f32
    len f32
}

This struct, however, just simply a branch of our tree.

fn main() {
    mut app := &App{
        gg: 0
    }

    app.gg = gg.new_context({
        bg_color: gx.rgba(255, 155, 155, 0)
        width: win_width
        height: win_height
        use_ortho: true
        create_window: true
        window_title: 'Tree Vv'
        keydown_fn: frame
        user_data: app
    })

    app.gg.run()
}

This is more fun, but to keep this simple, just know that this will create our paint brush.

fn frame(c sapp.KeyCode, m sapp.Modifier, app &App) {
    b1 := &Branch{
        x: f32(win_width / 2)
        y: f32(win_height)
        cx: f32(win_width / 2)
        cy: f32(win_height)
        rad: f32(math.pi / 2)
        len: rand.f32n(100)
    }

    app.gg.begin()
    app.draw_branch(b1, 1)
    app.draw_branch(b1, -1)
    app.gg.end()
}

The frame is where the fun happens! It's in charge of generating a new tree for every key-press.

We start with a simple branch, on X & Y positions, along with the old postions CX & CY.

We also generate the start radians, and a random length of the branch.

fn (app &App) draw_branch(b &Branch, mul f32) {
    cos := f32(math.cos(b.rad))
    sin := f32(math.sin(b.rad))

    x_tmp := b.cx - cos * b.len
    y_tmp := b.cy - sin * b.len

    app.gg.draw_line(b.cx, b.cy, x_tmp, y_tmp, gx.black)

    rad := rand.f32n(math.pi / 4)

    b1 := &Branch{
        x: b.cx
        y: b.cy
        cx: x_tmp
        cy: y_tmp
        rad: b.rad + rad * mul
        len: b.len - rand.f32n(25)
    }

    if b1.len > 10 {
        app.draw_branch(b1, 1)
        app.draw_branch(b1, -1)
    } else {
        app.gg.draw_circle(b1.cx, b1.cy, 4, gx.white)
    }
}

Here we simply calulate the branch's new X & Y positions. Re-use our old to as the new CX & CY.

We then draw a new line. Randomizes a number between 0 and PI / 4, and generaets a new rbanch from those values.

If the length of our branch is then above 10, we does the exact same thing again, becasue insanity, right?

If the branch's length is below or equals to 10 we draw a leaf.

Easy, right? The complete code is below my plug.

Shameless Plug

Before you turn to the complete code, please take a look at this beauty! placetree.art

I'd recommend you to click on the image to gain access to a tree placeholder!

Our Generator

The generator we've just created will of not be as beautiful as the above out-put, but it's a start nonetheless.

Screenshot from 2020-12-30 02-37-39.png

Complete Code

module main

import gg
import gx
import math
import rand
import sokol.sapp

const (
    win_width  = 600
    win_height = 600
)

struct App {
mut:
    gg    &gg.Context
}

struct Branch {
mut:
    x f32
    y f32
    cx f32
    cy f32
    rad f32
    len f32
}

fn main() {
    mut app := &App{
        gg: 0
    }

    app.gg = gg.new_context({
        bg_color: gx.rgba(255, 155, 155, 0)
        width: win_width
        height: win_height
        use_ortho: true
        create_window: true
        window_title: 'Tree Vv'
        keydown_fn: frame
        user_data: app
    })

    app.gg.run()
}

fn frame(c sapp.KeyCode, m sapp.Modifier, app &App) {
    b1 := &Branch{
        x: f32(win_width / 2)
        y: f32(win_height)
        cx: f32(win_width / 2)
        cy: f32(win_height)
        rad: f32(math.pi / 2)
        len: rand.f32n(100)
    }

    app.gg.begin()
    app.draw_branch(b1, 1)
    app.draw_branch(b1, -1)
    app.gg.end()
}

fn (app &App) draw_branch(b &Branch, mul f32) {
    cos := f32(math.cos(b.rad))
    sin := f32(math.sin(b.rad))

    x_tmp := b.cx - cos * b.len
    y_tmp := b.cy - sin * b.len

    app.gg.draw_line(b.cx, b.cy, x_tmp, y_tmp, gx.black)

    rad := rand.f32n(math.pi / 4)

    b1 := &Branch{
        x: b.cx
        y: b.cy
        cx: x_tmp
        cy: y_tmp
        rad: b.rad + rad * mul
        len: b.len - rand.f32n(25)
    }

    if b1.len > 10 {
        app.draw_branch(b1, 1)
        app.draw_branch(b1, -1)
    } else {
        app.gg.draw_circle(b1.cx, b1.cy, 4, gx.white)
    }
}


{ Best, Mads Bram Cordes }

No Comments Yet