rkgk/docs/haku.dj

125 lines
4.5 KiB
Plaintext

# haku
haku is a little scripting language used by rakugaki for programming brushes.
Here's a brief tour of the language.
## Your brush
Your brush is a piece of code that describes what's to be drawn on the wall.
For example, the default brush:
```haku
(stroke
8
(rgba 0 0 0 1)
(vec 0 0))
```
This is the simplest brush you can write.
It demonstrates a few things:
- The brush's task is to produce a description of what's to be drawn.
Brushes produce *scribbles* - commands that instruct rakugaki draw something on the wall.
- This brush produces the `stroke` scribble.
This scribble is composed out of three things:
- The stroke thickness - in this case `8`.
- The stroke color - in this case `(rgba 0 0 0 1)`.
Note that unlike most drawing programs, rakugaki brushes represent color channels with decimal numbers from 0 to 1, rather than integers from 0 to 255.
- The shape to draw - in this case a `(vec 0 0)`.
- Vectors are aggregations of four generic decimal numbers, most often used to represent positions in the wall's Cartesian coordinate space.
Although vectors are mathematically not the same as points, brushes always execute in a coordinate space relative to where you want to draw with the brush, so a separate `(point)` type isn't needed.
- Vectors in haku are four-dimensional, but the wall is two-dimensional, so the extra dimensions are discarded when drawing to the wall.
- haku permits constructing vectors from zero two four values - from `(vec)`, up to `(vec x y w h)`.
Any values that you leave out end up being zero.
- Note that a brush can only produce *one* scribble - this is because scribbles may be composed together using lists (described later.)
I highly recommend that you play around with the brush to get a feel for editing haku code!
## More complicated brushes
To make our brush more complicated, we can make it produce _multiple_ scribbles instead of just one.
To do that, we'll aggregate our scribbles into a _list_:
```haku
(list
(stroke 8 (rgba 0 0 1 1) (vec (- 4) 0))
(stroke 8 (rgba 1 0 0 1) (vec 4 0)))
```
A list allows us to say, "I'd like this brush to draw this, this, and this."
Of course, we are not limited to two elements only.
Here we'll use the `circle` function instead of `vec` to draw outlined circles.
```haku
(list
(stroke 1 (rgba 0 0 0 1) (circle (- 8) 0 8))
(stroke 1 (rgba 0 0 0 1) (circle 8 0 8))
(stroke 1 (rgba 0 0 0 1) (circle 0 (- 8) 8))
(stroke 1 (rgba 0 0 0 1) (circle 0 8 8)))
```
But the moment we'll want to change any one of these values, such as the color... we'll have to edit _every_ single occurrence of it!
To solve this, we can use a definition, or _def_ for short.
We'll replace our literal colors with a shared def:
```haku
(def color (rgba 0 0 0 1))
(list
(stroke 1 color (circle (- 8) 0 8))
(stroke 1 color (circle 8 0 8))
(stroke 1 color (circle 0 (- 8) 8))
(stroke 1 color (circle 0 8 8)))
```
And now we can control the color of the entire brush, without having to copy and paste it everywhere!
You can try doing this to the other `stroke` parameters, too - try creating a def `thickness` for the stroke thickness, `distance` for distance from the mouse, and `radius` for the radius of the circles.
## Repeating
At this point, we may be happy with this implementation.
But what if we want to draw four of these shapes, with different colors, next to each other?
This is where _functions_ come in.
A function allows us to store a piece of code for later, and give it some _parameters_, that'll be filled into the resulting value whenever the function is used.
```haku
(def circles
(fn (color x y)
(list
(stroke 1 color (circle (- x 8) y 8))
(stroke 1 color (circle (+ x 8) y 8))
(stroke 1 color (circle x (- y 8) 8))
(stroke 1 color (circle x (+ y 8) 8)))))
(circles (rgba 0 0 0 1) 0 0)
```
## Limits
The wall is infinite, but your brush may only draw in a small area around your cursor (~500 pixels.)
Drawing outside this area may result in pixels getting dropped in ugly ways, but it can also be used to your advantage in order to produce cool glitch art.
You can see this in action by setting the brush size to something really large:
```haku
(stroke
1000
(rgba 0 0 0 1)
(vec))
```
Additionally, haku code has some pretty strong limitations on what it can do.
It cannot be too big, it cannot execute for too long, and it cannot consume too much memory.
It does not have access to the world outside the wall, so you cannot use it to fire network requests or read the user's input in uncontrolled ways.