update documentation to include reticles and vector math
It's probably not very good though.
This commit is contained in:
parent
37c575748b
commit
fd3f37d744
2 changed files with 339 additions and 170 deletions
372
docs/rkgk.dj
372
docs/rkgk.dj
|
@ -86,8 +86,8 @@ If you want to draw multiple scribbles, you can wrap them into a list, which we
|
||||||
-- Draw two colorful dots instead of one!
|
-- Draw two colorful dots instead of one!
|
||||||
withDotter \d ->
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke 8 #F00 (vec ((vecX (d To)) + 4) (vecY (d To)))
|
stroke 8 #F00 (d To + vec 4 0)
|
||||||
stroke 8 #00F (vec ((vecX (d To)) - 4) (vecY (d To)))
|
stroke 8 #00F (d To + vec (-4) 0)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -108,26 +108,23 @@ It'll draw the first inner list, which contains two scribbles, and then it'll dr
|
||||||
withDotter \d ->
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
stroke 8 #F00 (vec ((vecX (d To)) + 4) (vecY (d To)))
|
stroke 8 #F00 (d To + vec 4 0)
|
||||||
stroke 8 #00F (vec ((vecX (d To)) - 4) (vecY (d To)))
|
stroke 8 #00F (d To + vec (-4) 0)
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
stroke 8 #FF0 (vec (vecX (d To)) ((vecY (d To)) + 4))
|
stroke 8 #FF0 (d To + vec 0 4)
|
||||||
stroke 8 #0FF (vec (vecX (d To)) ((vecY (d To)) - 4))
|
stroke 8 #0FF (d To + vec 0 (-4))
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
I know this example is kind of horrendous to read right now.
|
Another weird thing: when negating a number, you have to put it in parentheses.
|
||||||
|
|
||||||
This is because haku currently does not have vector math, which means we have to disassemble and reassemble vectors manually.
|
This is because haku does not see your spaces---`vec -4`, `vec - 4`, and `vec-4` all mean the same thing!
|
||||||
|
In this case, it will always choose the 2nd interpretation---vec minus four.
|
||||||
{% Another weird thing: when negating a number, you have to put it in parentheses. %}
|
So to make it interpret our minus four as, well, _minus four_, we need to enclose it in parentheses.
|
||||||
{% This is because haku does not see your spaces---`vec -4`, `vec - 4`, and `vec-4` all mean the same thing! %}
|
|
||||||
{% In this case, it will always choose the 2nd interpretation---vec minus four. %}
|
|
||||||
{% So to make it interpret our minus four as, well, _minus four_, we need to enclose it in parentheses. %}
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@ -163,7 +160,7 @@ If you reorder or remove any one of them, your brush isn't going to work!
|
||||||
- Then, `\d ->` lets us name the data we get back from the UI.
|
- Then, `\d ->` lets us name the data we get back from the UI.
|
||||||
`d` ends up containing a few useful properties, but the most useful one for us is `To`, which contains the current mouse position (where *`To`* draw).
|
`d` ends up containing a few useful properties, but the most useful one for us is `To`, which contains the current mouse position (where *`To`* draw).
|
||||||
|
|
||||||
- We'll get to what all these sigils mean to haku later!
|
- We'll get to what all these `\` and `->` sigils mean to haku later!
|
||||||
|
|
||||||
- On the next line we have a `stroke`.
|
- On the next line we have a `stroke`.
|
||||||
`stroke` is a _function_---a recipe for producing data!\
|
`stroke` is a _function_---a recipe for producing data!\
|
||||||
|
@ -200,11 +197,18 @@ Likewise, negative X coordinates go leftwards, and negative Y coordinates go upw
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Going back to the example though, `vec` is yet another function, except instead of producing strokes, it produces vectors!
|
Vectors in haku are obtained with another function---`vec`---though we don't use it in the basic example, because `d To` already is a vector.
|
||||||
|
Vectors support all the usual math operators though, so if we wanted to, we could, for example, add a vector to `d To`, thus moving the position of the dot relative to the mouse cursor:
|
||||||
|
|
||||||
Note how it's parenthesized though---recall that function arguments are separated with spaces, so if we didn't parenthesize the `vec`, we'd end up passing `vec`, `0`, and `0` back to `stroke`---which is far from what we want!
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
|
stroke 8 #000 (d To + vec 10 0) -- moved 10 pixels rightwards
|
||||||
|
```
|
||||||
|
|
||||||
And with all that, we let haku mix all the ingredients together, and get a black dot under the cursor.
|
Also note how the `d To` expression is parenthesized.
|
||||||
|
This is because otherwise, its individual parts would be interpreted as separate arguments to `stroke`, which is not what we want!
|
||||||
|
|
||||||
|
Anyways, with all that, we let haku mix all the ingredients together, and get a black dot under the cursor.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
withDotter \d ->
|
withDotter \d ->
|
||||||
|
@ -233,21 +237,23 @@ withDotter \d ->
|
||||||
We replace the singular position `d To` with a `line`. `line` expects two arguments, which are vectors defining the line's start and end points.
|
We replace the singular position `d To` with a `line`. `line` expects two arguments, which are vectors defining the line's start and end points.
|
||||||
For the starting position we use a _different_ property of `d`, which is `From`---this is the _previous_ value of `To`, which allows us to draw a continuous line.
|
For the starting position we use a _different_ property of `d`, which is `From`---this is the _previous_ value of `To`, which allows us to draw a continuous line.
|
||||||
|
|
||||||
```haku
|
|
||||||
[
|
|
||||||
stroke 8 #F00 (circle (-16) 0 16)
|
|
||||||
stroke 8 #00F (rect 0 (-16) 32 32)
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
In haku, by adding thickness to a point, it becomes a square.
|
In haku, by adding thickness to a point, it becomes a circle.
|
||||||
In theory it could also become a circle...
|
In theory it could also become a square... but if line caps are rounded, they connect together much more nicely!
|
||||||
But let's not go down that rabbit hole.
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
haku also supports other kinds of shapes: circles and rectangles.
|
||||||
|
|
||||||
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
|
[
|
||||||
|
stroke 8 #F00 (circle (d To + vec (-16) 0) 16)
|
||||||
|
stroke 8 #00F (rect (d To + vec 0 (-16)) 32 32)
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
- `circle`s are made up of an X position, Y position, and radius.
|
- `circle`s are made up of an X position, Y position, and radius.
|
||||||
|
|
||||||
- `rect`s are made up of the (X and Y) position of their top-left corner, and a size (width and height).\
|
- `rect`s are made up of the (X and Y) position of their top-left corner, and a size (width and height).\
|
||||||
|
@ -261,9 +267,10 @@ But if describing data was all we ever wanted, we could've just used any ol' dra
|
||||||
Remember that example from before?
|
Remember that example from before?
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke 8 #F00 (vec 4 0)
|
stroke 8 #F00 (d To + vec 4 0)
|
||||||
stroke 8 #00F (vec (-4) 0)
|
stroke 8 #00F (d To + vec (-4) 0)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -271,9 +278,10 @@ It has quite a bit of repetition in it.
|
||||||
If we wanted to change the size of the points, we'd need to first update the stroke thickness...
|
If we wanted to change the size of the points, we'd need to first update the stroke thickness...
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke 4 #F00 (vec 4 0)
|
stroke 4 #F00 (d To + vec 4 0)
|
||||||
stroke 4 #00F (vec (-4) 0)
|
stroke 4 #00F (d To + vec (-4) 0)
|
||||||
---
|
---
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -284,9 +292,9 @@ So we also have to update their positions.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
[
|
[
|
||||||
stroke 4 #F00 (vec 2 0)
|
stroke 4 #F00 (d To + vec 2 0)
|
||||||
---
|
---
|
||||||
stroke 4 #00F (vec (-2) 0)
|
stroke 4 #00F (d To + vec (-2) 0)
|
||||||
--
|
--
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -311,9 +319,10 @@ So we can define `thickness` to be `4`, and then use it in our scribbles.
|
||||||
```haku
|
```haku
|
||||||
thickness = 4
|
thickness = 4
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke thickness #F00 (vec 2 0)
|
stroke thickness #F00 (d To + vec 2 0)
|
||||||
stroke thickness #00F (vec (-2) 0)
|
stroke thickness #00F (d To + vec (-2) 0)
|
||||||
---------
|
---------
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -327,15 +336,25 @@ To keep a consistent program structure, haku also forces all your defs to appear
|
||||||
You can think of the defs as a list of ingredients for the final scribble.
|
You can think of the defs as a list of ingredients for the final scribble.
|
||||||
Reading the ingredients can give you context as to what you're gonna be cooking, so it's useful to have them first!
|
Reading the ingredients can give you context as to what you're gonna be cooking, so it's useful to have them first!
|
||||||
|
|
||||||
|
If you find this hard to grasp, just know that _defs usually go before `withDotter`_.
|
||||||
|
|
||||||
|
::: aside
|
||||||
|
|
||||||
|
We cannot refer to `d` from defs, because `d` is only visible on the `withDotter` line and below.
|
||||||
|
We'll get to why soon!
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
Anyways, we can likewise replace our `2` constants with a def:
|
Anyways, we can likewise replace our `2` constants with a def:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
thickness = 4
|
thickness = 4
|
||||||
xOffset = 2
|
xOffset = 2
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke thickness #F00 (vec xOffset 0)
|
stroke thickness #F00 (d To + vec xOffset 0)
|
||||||
stroke thickness #00F (vec (-xOffset) 0)
|
stroke thickness #00F (d To + vec (-xOffset) 0)
|
||||||
---------
|
---------
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -347,9 +366,13 @@ This naming convention is known as `camelCase`, and is used everywhere throughou
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
Of note is that haku names also cannot start with an uppercase letter.
|
Of note is that haku names also cannot start with an uppercase letter.
|
||||||
It's reserved syntax for the future.
|
Uppercase names are special values we call _tags_.
|
||||||
|
|
||||||
Right now the only names that start with an uppercase letter are the two booleans, `True` and `False`.
|
Tags are values which represent names.
|
||||||
|
For example, the `To` in `d To` is a tag.
|
||||||
|
It represents the name of the piece of data we're extracting from `d`.
|
||||||
|
|
||||||
|
There are also two special tags, `True` and `False`, which represent [Boolean](https://en.wikipedia.org/wiki/Boolean_algebra) truth and falsehood.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@ -361,9 +384,10 @@ thickness = 8
|
||||||
---
|
---
|
||||||
xOffset = 2
|
xOffset = 2
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke thickness #F00 (vec xOffset 0)
|
stroke thickness #F00 (d To + vec xOffset 0)
|
||||||
stroke thickness #00F (vec (-xOffset) 0)
|
stroke thickness #00F (d To + vec (-xOffset) 0)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -374,9 +398,10 @@ thickness = 8
|
||||||
xOffset = thickness / 2
|
xOffset = thickness / 2
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
stroke thickness #F00 (vec xOffset 0)
|
stroke thickness #F00 (d To + vec xOffset 0)
|
||||||
stroke thickness #00F (vec (-xOffset) 0)
|
stroke thickness #00F (d To + vec (-xOffset) 0)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -389,14 +414,16 @@ So far we've only been dealing with strokes.
|
||||||
So why not switch it up a little and _fill in_ a shape?
|
So why not switch it up a little and _fill in_ a shape?
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
fill #000 (circle 0 0 16)
|
withDotter \d ->
|
||||||
|
fill #000 (circle (d To) 16)
|
||||||
```
|
```
|
||||||
|
|
||||||
How about... some transparency?
|
How about... some transparency?
|
||||||
Recall that colors can have an alpha component, so let's try using that!
|
Recall that colors can have an alpha component, so let's try using that!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
fill #0001 (circle 0 0 16)
|
withDotter \d ->
|
||||||
|
fill #0001 (circle (d To) 16)
|
||||||
```
|
```
|
||||||
|
|
||||||
If you play around with this brush, you'll notice how the circles blend together really nicely.
|
If you play around with this brush, you'll notice how the circles blend together really nicely.
|
||||||
|
@ -405,20 +432,22 @@ That's the power of Alpha!
|
||||||
Now let's see what happens if we draw two such circles on top of each other---one bigger, one smaller.
|
Now let's see what happens if we draw two such circles on top of each other---one bigger, one smaller.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
fill #0001 (circle 0 0 16)
|
fill #0001 (circle (d To) 16)
|
||||||
fill #0001 (circle 0 0 32)
|
fill #0001 (circle (d To) 32)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
How about four?
|
How about four?
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
fill #0001 (circle 0 0 8)
|
fill #0001 (circle (d To) 8)
|
||||||
fill #0001 (circle 0 0 16)
|
fill #0001 (circle (d To) 16)
|
||||||
fill #0001 (circle 0 0 24)
|
fill #0001 (circle (d To) 24)
|
||||||
fill #0001 (circle 0 0 32)
|
fill #0001 (circle (d To) 32)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -455,14 +484,15 @@ That'll need fixing!
|
||||||
Either way, let's define a function that'll make us those circles!
|
Either way, let's define a function that'll make us those circles!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #0001 (circle 0 0 radius)
|
fill #0001 (circle (d To) radius)
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
splat 8
|
splat d 8
|
||||||
splat 16
|
splat d 16
|
||||||
splat 24
|
splat d 24
|
||||||
splat 32
|
splat d 32
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -481,6 +511,10 @@ To dismantle that weird `\` syntax...
|
||||||
A function can have an arbitrary number of parameters listed, separated by commas, and that many parameters _must_ be passed to it.
|
A function can have an arbitrary number of parameters listed, separated by commas, and that many parameters _must_ be passed to it.
|
||||||
Otherwise your brush will fail with an error!
|
Otherwise your brush will fail with an error!
|
||||||
|
|
||||||
|
- Note how also pass `d` into `splat`.
|
||||||
|
If you're a keen-eyed observer, you will have already noticed that the `\d ->` after `withDotter` is a function, too!
|
||||||
|
And the values a function uses, such as `d To`, have to come from somewhere.
|
||||||
|
|
||||||
- And lastly, after an arrow `->`, we have the function's result.
|
- And lastly, after an arrow `->`, we have the function's result.
|
||||||
|
|
||||||
Note that a function can only have _one_ result, just like a brush can only have one scribble.
|
Note that a function can only have _one_ result, just like a brush can only have one scribble.
|
||||||
|
@ -500,18 +534,19 @@ haku limits the use of overloading to system functions for simplicity---adding o
|
||||||
Since these transparent circles are so much easier to draw now, let's make a few more of them!
|
Since these transparent circles are so much easier to draw now, let's make a few more of them!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #0001 (circle 0 0 radius)
|
fill #0001 (circle (d To) radius)
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
[
|
[
|
||||||
splat 8
|
splat d 8
|
||||||
splat 16
|
splat d 16
|
||||||
splat 24
|
splat d 24
|
||||||
splat 32
|
splat d 32
|
||||||
splat 40
|
splat d 40
|
||||||
splat 48
|
splat d 48
|
||||||
splat 56
|
splat d 56
|
||||||
splat 64
|
splat d 64
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -545,16 +580,17 @@ Until some threshold is reached, in which case we just make a single circle.
|
||||||
The first part is easy to do: haku allows us to define a function that calls itself without making any fuss.
|
The first part is easy to do: haku allows us to define a function that calls itself without making any fuss.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #0001 (circle 0 0 radius)
|
fill #0001 (circle (d To) radius)
|
||||||
|
|
||||||
airbrush = \size ->
|
airbrush = \d, size ->
|
||||||
[
|
[
|
||||||
splat size
|
splat d size
|
||||||
airbrush (size - 8)
|
airbrush d (size - 8)
|
||||||
]
|
]
|
||||||
|
|
||||||
airbrush 64 -- sounds like some Nintendo 64 game about graffiti, lol.
|
withDotter \d ->
|
||||||
|
airbrush d 64
|
||||||
```
|
```
|
||||||
|
|
||||||
But...
|
But...
|
||||||
|
@ -588,34 +624,37 @@ color =
|
||||||
else
|
else
|
||||||
#F00
|
#F00
|
||||||
|
|
||||||
fill color (circle 0 0 radius)
|
withDotter \d ->
|
||||||
|
fill color (circle (d To) radius)
|
||||||
```
|
```
|
||||||
|
|
||||||
- `<` is a function that produces `true` if the second argument is a smaller number than the first argument.
|
- `<` is a function that produces `true` if the second argument is a smaller number than the first argument.
|
||||||
|
|
||||||
Truth and falsehood are data too, and are represented with the values `true` and `false`.
|
Truth and falsehood are data too, and are represented with the values `True` and `False`.
|
||||||
|
|
||||||
- We need three arguments to execute an `if`: the condition, the data to use when the condition is `true`, and the data to use when the condition is `false`.
|
- We need three arguments to execute an `if`: the condition, the data to use when the condition is `True`, and the data to use when the condition is `False`.
|
||||||
|
|
||||||
What's magical about an `if` is that _only one branch is executed_.
|
What's magical about an `if` is that _only one branch is executed_.
|
||||||
|
|
||||||
In a function call, all arguments will always be calculated.
|
In a function call, all arguments will always be calculated.
|
||||||
An `if` only calculates the argument it needs to produce the result.
|
An `if` only calculates the argument it needs to produce the result.
|
||||||
This allows us to use it to prevent unbounded recursion in our `airbrush` example.
|
This allows us to use it to prevent unbounded recursion in our `airbrush` example.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #0001 (circle 0 0 radius)
|
fill #0001 (circle (d To) radius)
|
||||||
|
|
||||||
airbrush = \size ->
|
airbrush = \d, size ->
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
[
|
[
|
||||||
splat size
|
splat d size
|
||||||
airbrush (size - 8)
|
airbrush d (size - 8)
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
||||||
airbrush 64
|
withDotter \d ->
|
||||||
|
airbrush d 64
|
||||||
```
|
```
|
||||||
|
|
||||||
Neat!
|
Neat!
|
||||||
|
@ -628,39 +667,41 @@ But the airbrush still looks super primitive.
|
||||||
Let's try increasing the fidelity by doing smaller steps!
|
Let's try increasing the fidelity by doing smaller steps!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #0001 (circle 0 0 radius)
|
fill #0001 (circle (d To) radius)
|
||||||
|
|
||||||
airbrush = \size ->
|
airbrush = \d, size ->
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
[
|
[
|
||||||
splat size
|
splat d size
|
||||||
airbrush (size - 1)
|
airbrush d (size - 1)
|
||||||
---
|
---
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
||||||
airbrush 64
|
withDotter \d ->
|
||||||
|
airbrush d 64
|
||||||
```
|
```
|
||||||
|
|
||||||
Well... sure, that's just a black blob with a slight gradient on the outer edge, so let's decrease the opacity.
|
Well... sure, that's just a black blob with a slight gradient on the outer edge, so let's decrease the opacity.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
splat = \radius ->
|
splat = \d, radius ->
|
||||||
fill #00000004 (circle 0 0 radius)
|
fill #00000004 (circle (d To) radius)
|
||||||
---------
|
---------
|
||||||
|
|
||||||
airbrush = \size ->
|
airbrush = \d, size ->
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
[
|
[
|
||||||
splat size
|
splat d size
|
||||||
airbrush (size - 1)
|
airbrush d (size - 1)
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
||||||
airbrush 64
|
withDotter \d ->
|
||||||
|
airbrush d 64
|
||||||
```
|
```
|
||||||
|
|
||||||
Looks good as a single dot, but if you try drawing with it... it's gray??
|
Looks good as a single dot, but if you try drawing with it... it's gray??
|
||||||
|
@ -698,8 +739,9 @@ Most commonly, colors are blended using _linear interpolation_---which is essent
|
||||||
|
|
||||||
Mathematically, linear interpolation is defined using this formula:
|
Mathematically, linear interpolation is defined using this formula:
|
||||||
|
|
||||||
```
|
```haku
|
||||||
lerp(a, b, t) = a + (b - a) * t
|
lerp = \a, b, t ->
|
||||||
|
a + (b - a) * t
|
||||||
```
|
```
|
||||||
|
|
||||||
What we're doing when blending colors, is mixing between a _source_ color (the wall), and a _destination_ color (the brush) on each channel.
|
What we're doing when blending colors, is mixing between a _source_ color (the wall), and a _destination_ color (the brush) on each channel.
|
||||||
|
@ -710,8 +752,8 @@ But due to this reduced precision on the wall, we have to convert from a real nu
|
||||||
Consider that we're drawing circles of opacity 0.01 every single time.
|
Consider that we're drawing circles of opacity 0.01 every single time.
|
||||||
Now let's look what happens when we try to blend each circle on top of a single pixel...
|
Now let's look what happens when we try to blend each circle on top of a single pixel...
|
||||||
|
|
||||||
```
|
```haku
|
||||||
lerp(0, 255, 0.01) = 0 + (255 - 0) * 0.01 = 255 * 0.01 = 2.55
|
lerp 0 255 0.01 = 0 + (255 - 0) * 0.01 = 255 * 0.01 = 2.55
|
||||||
```
|
```
|
||||||
|
|
||||||
That's one circle.
|
That's one circle.
|
||||||
|
@ -727,33 +769,33 @@ For negative results, it gives different results: `floor(-1.5)` would be `-2`, w
|
||||||
|
|
||||||
So for the next step, we'll be interpolating from `2`, and not `2.55`...
|
So for the next step, we'll be interpolating from `2`, and not `2.55`...
|
||||||
|
|
||||||
```
|
```haku
|
||||||
lerp(2, 255, 0.01) = 4.53
|
lerp 2 255 0.01 = 4.53
|
||||||
lerp(4, 255, 0.01) = 6.51
|
lerp 4 255 0.01 = 6.51
|
||||||
lerp(6, 255, 0.01) = 8.49
|
lerp 6 255 0.01 = 8.49
|
||||||
lerp(8, 255, 0.01) = 10.47
|
lerp 8 255 0.01 = 10.47
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
I think you can see the pattern here.
|
I think you can see the pattern here.
|
||||||
This continues until around 52, where the decimal point finally goes below zero, and now we're incrementing by one instead.
|
This continues until around 52, where the decimal point finally goes below zero, and now we're incrementing by one instead.
|
||||||
|
|
||||||
```
|
```haku
|
||||||
...
|
...
|
||||||
lerp(52, 255, 0.01) = 54.03
|
lerp 52 255 0.01 = 54.03
|
||||||
lerp(54, 255, 0.01) = 56.01
|
lerp 54 255 0.01 = 56.01
|
||||||
lerp(56, 255, 0.01) = 57.99 -- !!
|
lerp 56 255 0.01 = 57.99 -- !!
|
||||||
lerp(57, 255, 0.01) = 58.98
|
lerp 57 255 0.01 = 58.98
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
...and at one point, we get to this:
|
...and at one point, we get to this:
|
||||||
|
|
||||||
```
|
```haku
|
||||||
lerp(153, 255, 0.01) = 154.02
|
lerp 153 255 0.01 = 154.02
|
||||||
lerp(154, 255, 0.01) = 155.01
|
lerp 154 255 0.01 = 155.01
|
||||||
lerp(155, 255, 0.01) = 156
|
lerp 155 255 0.01 = 156
|
||||||
lerp(156, 255, 0.01) = 156.99 -- !!
|
lerp 156 255 0.01 = 156.99 -- !!
|
||||||
```
|
```
|
||||||
|
|
||||||
Truncating 156.99 will get us to 156 again, which means we're stuck!
|
Truncating 156.99 will get us to 156 again, which means we're stuck!
|
||||||
|
@ -762,7 +804,7 @@ This precision limitation is quite unfortunate, but I don't have a solution for
|
||||||
Maybe one day.
|
Maybe one day.
|
||||||
For now you'll have to construct your brushes with this in mind.
|
For now you'll have to construct your brushes with this in mind.
|
||||||
|
|
||||||
## And more limits
|
### And more limits
|
||||||
|
|
||||||
There are more limits on top of this, which stem from haku's design.
|
There are more limits on top of this, which stem from haku's design.
|
||||||
Since it's running _your_ code on _my_ server, it has some arbitrary limits set to prevent it from causing much harm.
|
Since it's running _your_ code on _my_ server, it has some arbitrary limits set to prevent it from causing much harm.
|
||||||
|
@ -771,14 +813,108 @@ haku code cannot be too long, and it cannot execute too long.
|
||||||
It cannot consume too much memory---you cannot have too many definitions, or too many temporary values at once.
|
It cannot consume too much memory---you cannot have too many definitions, or too many temporary values at once.
|
||||||
There are also memory usage limits on "heavyweight" data, such as functions or lists.
|
There are also memory usage limits on "heavyweight" data, such as functions or lists.
|
||||||
|
|
||||||
Basically, don't DoS me with it ^^'
|
|
||||||
|
|
||||||
I'm not specifying the precise limits here, because the app will show these to you in the future.
|
## Reticles
|
||||||
There's no point in documenting them if you can't inspect your brush's resource usage easily.
|
|
||||||
|
Having basic knowledge of functions and scribbles, you may be wondering: _what does that `withDotter` function do?_
|
||||||
|
It surely looks a bit magical, conjuring that `d` parameter from nowhere; and `d` contains everything we need to draw!
|
||||||
|
|
||||||
|
Put simply, `withDotter` is what rakugaki calls a *reticle*.
|
||||||
|
Reticles are pieces of data representing _interactions with the wall_.
|
||||||
|
|
||||||
|
A reticle is usually composed of two parts: the reticle data, defining how the interaction is meant to be initiated, and a _continuation function_.
|
||||||
|
When your brush gives rakugaki a reticle, it will take the reticle data, and let the user perform an interaction.
|
||||||
|
Once the interaction is performed, it will give whatever user input it has gathered as an argument to the continuation function, which can return a scribble to draw on the wall, or another reticle.
|
||||||
|
|
||||||
|
rakugaki will continue performing interactions until the brush gives back a scribble.
|
||||||
|
|
||||||
|
`withDotter` is the simplest, most direct reticle for interacting with the wall.
|
||||||
|
It allows you to paint freely, and gives the brush information on your current mouse position, previous mouse position, and _number of steps_ performed thus far.
|
||||||
|
|
||||||
|
You've already seen the former two properties---those are `d To` and `d From`.
|
||||||
|
The number of steps is available as `d Num`.
|
||||||
|
|
||||||
|
This is an integer starting at 0, incremented by 1 with each execution of the brush, until you release your mouse cursor.
|
||||||
|
This allows you to _animate_ your brushes over time!
|
||||||
|
For example, this brush draws a rainbow.
|
||||||
|
|
||||||
|
```haku
|
||||||
|
colorCurve = \n ->
|
||||||
|
abs (cos n)
|
||||||
|
|
||||||
|
pi = 3.14159265
|
||||||
|
l = 0.1 -- wavelength
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
|
let r = colorCurve (d Num * l)
|
||||||
|
let g = colorCurve (d Num * l + pi/3)
|
||||||
|
let b = colorCurve (d Num * l + 2*pi/3)
|
||||||
|
let color = rgba r g b 1
|
||||||
|
stroke 8 color (line (d From) (d To))
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently, `withDotter` is the only reticle available in rakugaki, and it cannot be chained due to its immediate nature:
|
||||||
|
`withDotter` continues executing immediately after you move your mouse by the _tiniest_ bit, so it's unclear how to even continue after that!
|
||||||
|
|
||||||
|
In the future rakugaki might get reticles that let you select lines, rectangles, ellipses, curves... but today is not that day.
|
||||||
|
|
||||||
|
|
||||||
|
### What's that, `let`?
|
||||||
|
|
||||||
|
I mentioned before that you cannot have defs inside functions.
|
||||||
|
What you _can_ have though, is `let`s, which define _variables_.
|
||||||
|
|
||||||
|
Unlike defs, which are constant and cannot vary, variables' values can depend on function parameters---and a function can be called with a different set of parameters each time, thus making them variable!
|
||||||
|
|
||||||
|
A `let` always takes the following form.
|
||||||
|
|
||||||
|
```haku
|
||||||
|
let name = value
|
||||||
|
then
|
||||||
|
```
|
||||||
|
|
||||||
|
It's very similar to a def, with one major difference.
|
||||||
|
Because a `let` by itself only _names a value_ and does not have a result, it must be followed by another expression on the following line---and that expression determines the result.
|
||||||
|
The magic is that this continuing expression can refer to the `name` we had previously assigned in the `let` expression.
|
||||||
|
|
||||||
|
::: aside
|
||||||
|
|
||||||
|
Here's a bit of trivia: the variable defined by a `let` is exactly the same as a function parameter.
|
||||||
|
The `let` above is equivalent to applying the argument `value` to a function taking in the parameter `name`, and returning `then` as the result.
|
||||||
|
|
||||||
|
```haku
|
||||||
|
(\name -> then) value
|
||||||
|
```
|
||||||
|
|
||||||
|
This basic little trick with immediately applying a function stands the basis of a formal mathematical system called [_lambda calculus_](https://en.wikipedia.org/wiki/Lambda_calculus).
|
||||||
|
It underpins a large number of functional programming languages, including haku, and more famously [Haskell](https://www.haskell.org/)!
|
||||||
|
|
||||||
|
That's right. haku is a cute little Haskell for artists.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
`let`s aren't only useful for reusability---they're also helpful for breaking your brushes into smaller, more digestible pieces!
|
||||||
|
Compare the above version of the rainbow brush to this version, where all the `let`s are written inline:
|
||||||
|
|
||||||
|
```haku
|
||||||
|
colorCurve = \n ->
|
||||||
|
abs (cos n)
|
||||||
|
|
||||||
|
pi = 3.14159265
|
||||||
|
l = 0.1 -- wavelength
|
||||||
|
|
||||||
|
withDotter \d ->
|
||||||
|
stroke 8 (rgba (colorCurve (d Num * l)) (colorCurve (d Num * l + pi/3)) (colorCurve (d Num * l + 2*pi/3)) 1) (line (d From) (d To))
|
||||||
|
```
|
||||||
|
|
||||||
|
That's one hard to read beast of a `stroke`!
|
||||||
|
|
||||||
|
Generally, if a line is so long it wraps around rakugaki's narrow little text editor, it's probably a good idea to split it into variables.
|
||||||
|
|
||||||
|
|
||||||
## Have fun
|
## Have fun
|
||||||
|
|
||||||
With that said, I hope you can have fun with rakugaki despite its flaws.
|
With that said, I hope you can have fun with rakugaki despite it being in its infancy!
|
||||||
|
|
||||||
You may want to check out the [system library reference](/docs/system.html) now, to know what else you can do with the language---this little introduction barely even scratched the surface of what's possible!
|
You may want to check out the [system library reference](/docs/system.html) now, to know what else you can do with the language---this little introduction barely even scratched the surface of what's possible!
|
||||||
|
|
||||||
|
|
|
@ -63,45 +63,96 @@ Additionally, the syntax `a | b` may be used to signify that one of the listed t
|
||||||
-
|
-
|
||||||
a : number
|
a : number
|
||||||
-> number
|
-> number
|
||||||
|
|
||||||
|
-
|
||||||
|
a : vector
|
||||||
|
-> vector
|
||||||
```
|
```
|
||||||
|
|
||||||
`-`, when used in its unary form `-x`, returns the number `x` with the opposite sign.
|
`-`, when used in its unary form `-x`, returns the number `x` with the opposite sign.
|
||||||
|
When used on vectors, returns the same vector facing the reverse direction (the individual components are negated.)
|
||||||
|
|
||||||
|
This operation is not defined for colors, because it doesn't make sense to have a color with negative RGBA values.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
+
|
+
|
||||||
a : number
|
a : number
|
||||||
b : number
|
b : number
|
||||||
-> number
|
-> number
|
||||||
|
|
||||||
|
+
|
||||||
|
a : vector
|
||||||
|
b : vector
|
||||||
|
-> vector
|
||||||
|
|
||||||
|
+
|
||||||
|
a : rgba
|
||||||
|
b : rgba
|
||||||
|
-> rgba
|
||||||
```
|
```
|
||||||
|
|
||||||
`+` adds two numbers together.
|
`+` adds two numbers, vectors, or colors together.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
-
|
-
|
||||||
a : number
|
a : number
|
||||||
b : number
|
b : number
|
||||||
-> number
|
-> number
|
||||||
|
|
||||||
|
-
|
||||||
|
a : vector
|
||||||
|
b : vector
|
||||||
|
-> vector
|
||||||
|
|
||||||
|
-
|
||||||
|
a : rgba
|
||||||
|
b : rgba
|
||||||
|
-> rgba
|
||||||
```
|
```
|
||||||
|
|
||||||
`-`, when used in its binary form `x - y`, subtracts two numbers from one another.
|
`-`, when used in its binary form `x - y`, subtracts two numbers, vectors, or colors from one another.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
*
|
*
|
||||||
a : number
|
a : number
|
||||||
b : number
|
b : number
|
||||||
-> number
|
-> number
|
||||||
|
|
||||||
|
*
|
||||||
|
a : vector
|
||||||
|
b : vector
|
||||||
|
-> vector
|
||||||
|
|
||||||
|
*
|
||||||
|
a : rgba
|
||||||
|
b : rgba
|
||||||
|
-> rgba
|
||||||
```
|
```
|
||||||
|
|
||||||
`*` multiplies two numbers together.
|
`*` multiplies two numbers together.
|
||||||
|
When used on vectors, it scales them component-wise.
|
||||||
|
Likewise for colors.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
/
|
/
|
||||||
a : number
|
a : number
|
||||||
b : number
|
b : number
|
||||||
-> number
|
-> number
|
||||||
|
|
||||||
|
/
|
||||||
|
a : vector
|
||||||
|
b : vector
|
||||||
|
-> vector
|
||||||
|
|
||||||
|
/
|
||||||
|
a : rgba
|
||||||
|
b : rgba
|
||||||
|
-> rgba
|
||||||
```
|
```
|
||||||
|
|
||||||
`/` divides a number by another number.
|
`/` divides a number by another number.
|
||||||
|
When used on vectors, it divides them component-wise.
|
||||||
|
Likewise for colors.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
floor
|
floor
|
||||||
|
@ -324,7 +375,7 @@ The following functions are used to compare values and work with `boolean`s.
|
||||||
-> boolean
|
-> boolean
|
||||||
```
|
```
|
||||||
|
|
||||||
If `b` is `()` or `False`, `not` returns `true`.
|
If `b` is `()` or `False`, `not` returns `True`.
|
||||||
Otherwise it returns `False`.
|
Otherwise it returns `False`.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
@ -459,25 +510,6 @@ vecW
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Note that mathematical operations are currently not defined for vectors.
|
|
||||||
You may define your own vector operations like so:
|
|
||||||
|
|
||||||
```haku
|
|
||||||
-- Vector addition
|
|
||||||
addv = \a, b ->
|
|
||||||
vec (vecX a + vecX b) (vecY a + vecY b) (vecZ a + vecZ b) (vecW a + vecW b)
|
|
||||||
|
|
||||||
-- Likewise for subtraction, multiplication, and division.
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that haku-defined vector operations like these are more costly the more components they operate on.
|
|
||||||
Therefore, it's recommended to only define them for two dimensions, unless you really need more.
|
|
||||||
|
|
||||||
```haku
|
|
||||||
addv2 = \a, b ->
|
|
||||||
vec (vecX a + vecX b) (vecY a + vecY b)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Colors
|
## Colors
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
|
@ -520,6 +552,7 @@ For example, consider multiplicatively blending two colors.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
-- This is how you can multiply two colors together.
|
-- This is how you can multiply two colors together.
|
||||||
|
-- Note that the `*` operator works for colors, so you don't need to define this in your brushes.
|
||||||
mulRgba = \a, b ->
|
mulRgba = \a, b ->
|
||||||
rgba (rgbaR a * rgbaR b) (rgbaG a * rgbaG b) (rgbaB a * rgbaB b) (rgbaA a * rgbaA b)
|
rgba (rgbaR a * rgbaR b) (rgbaG a * rgbaG b) (rgbaB a * rgbaB b) (rgbaA a * rgbaA b)
|
||||||
```
|
```
|
||||||
|
@ -539,12 +572,12 @@ mulRgba = \a, b ->
|
||||||
Note that haku does not clamp colors to the `0` to `1` range.
|
Note that haku does not clamp colors to the `0` to `1` range.
|
||||||
It is perfectly valid to have a color that is out of range or even `NaN`, but when drawing scribbles:
|
It is perfectly valid to have a color that is out of range or even `NaN`, but when drawing scribbles:
|
||||||
|
|
||||||
|
- any number less than `0` is clamped to `0`.
|
||||||
|
- any number greater than `1` is clamped to `1`.
|
||||||
- `∞` is clamped back to `1`.
|
- `∞` is clamped back to `1`.
|
||||||
- `-∞` is clamped back to `0`.
|
- `-∞` is clamped back to `0`.
|
||||||
- any scribble with a `NaN` color is ignored.
|
- any scribble with a `NaN` color is ignored.
|
||||||
|
|
||||||
Note that just like vectors, arithmetic operations on colors are currently not defined.
|
|
||||||
|
|
||||||
Before scribbles are drawn to the wall, colors are converted to 8-bit integers for more efficient rasterization and storage.
|
Before scribbles are drawn to the wall, colors are converted to 8-bit integers for more efficient rasterization and storage.
|
||||||
This means some loss of precision will happen, which may cause issues with brushes like this one:
|
This means some loss of precision will happen, which may cause issues with brushes like this one:
|
||||||
|
|
||||||
|
@ -625,7 +658,7 @@ stroke
|
||||||
|
|
||||||
Creates a stroke scribble, which outlines the provided shape with a stroke of the given thickness and color.
|
Creates a stroke scribble, which outlines the provided shape with a stroke of the given thickness and color.
|
||||||
|
|
||||||
Point shapes are drawn as squares, and `line` shapes have square caps at the line's endpoints.
|
Point shapes are drawn as circles, and `line` shapes have round caps at the line's endpoints.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
fill
|
fill
|
||||||
|
|
Loading…
Reference in a new issue