fix more syntax v2 bugs, update docs
This commit is contained in:
parent
bf37d7305c
commit
d1a6fb364e
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -570,6 +570,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
name = "haku"
|
name = "haku"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -465,6 +465,8 @@ unsafe extern "C" fn haku_render_value(
|
||||||
debug!("resetting exception");
|
debug!("resetting exception");
|
||||||
instance.exception = None;
|
instance.exception = None;
|
||||||
|
|
||||||
|
debug!("will render value: {:?}", instance.value);
|
||||||
|
|
||||||
let pixmap_locked = &mut (*pixmap).pixmap;
|
let pixmap_locked = &mut (*pixmap).pixmap;
|
||||||
|
|
||||||
let mut renderer = Renderer::new(
|
let mut renderer = Renderer::new(
|
||||||
|
|
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
log.workspace = true
|
||||||
tiny-skia = { version = "0.11.4", default-features = false, features = ["no-std-float"] }
|
tiny-skia = { version = "0.11.4", default-features = false, features = ["no-std-float"] }
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub enum Opcode {
|
||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
Number, // (float: f32)
|
Number, // (float: f32)
|
||||||
|
Rgba, // (r: u8, g: u8, b: u8, a: u8)
|
||||||
|
|
||||||
// Duplicate existing values.
|
// Duplicate existing values.
|
||||||
/// Push a value relative to the bottom of the current stack window.
|
/// Push a value relative to the bottom of the current stack window.
|
||||||
|
@ -26,6 +27,9 @@ pub enum Opcode {
|
||||||
/// Set the value of a definition.
|
/// Set the value of a definition.
|
||||||
SetDef, // (index: u16)
|
SetDef, // (index: u16)
|
||||||
|
|
||||||
|
// Create lists.
|
||||||
|
List, // (len: u16)
|
||||||
|
|
||||||
// Create literal functions.
|
// Create literal functions.
|
||||||
Function, // (params: u8, then: u16), at `then`: (local_count: u8, capture_count: u8, captures: [(source: u8, index: u8); capture_count])
|
Function, // (params: u8, then: u16), at `then`: (local_count: u8, capture_count: u8, captures: [(source: u8, index: u8); capture_count])
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ use core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Ast, NodeId, NodeKind},
|
ast::{Ast, NodeId, NodeKind},
|
||||||
|
@ -98,12 +99,11 @@ pub fn compile_expr<'a>(c: &mut Compiler<'a>, src: &Source<'a>, node_id: NodeId)
|
||||||
// as they may also contain commas and other trivia.
|
// as they may also contain commas and other trivia.
|
||||||
NodeKind::Param => unreachable!("Param node should never be emitted"),
|
NodeKind::Param => unreachable!("Param node should never be emitted"),
|
||||||
|
|
||||||
NodeKind::Color => unsupported(c, src, node_id, "color literals are not implemented yet"),
|
|
||||||
|
|
||||||
NodeKind::Ident => compile_ident(c, src, node_id),
|
NodeKind::Ident => compile_ident(c, src, node_id),
|
||||||
NodeKind::Number => compile_number(c, src, node_id),
|
|
||||||
NodeKind::Tag => compile_tag(c, src, node_id),
|
NodeKind::Tag => compile_tag(c, src, node_id),
|
||||||
NodeKind::List => unsupported(c, src, node_id, "list literals are not implemented yet"),
|
NodeKind::Number => compile_number(c, src, node_id),
|
||||||
|
NodeKind::Color => compile_color(c, src, node_id),
|
||||||
|
NodeKind::List => compile_list(c, src, node_id),
|
||||||
|
|
||||||
NodeKind::Unary => compile_unary(c, src, node_id),
|
NodeKind::Unary => compile_unary(c, src, node_id),
|
||||||
NodeKind::Binary => compile_binary(c, src, node_id),
|
NodeKind::Binary => compile_binary(c, src, node_id),
|
||||||
|
@ -200,18 +200,6 @@ fn compile_ident<'a>(c: &mut Compiler<'a>, src: &Source<'a>, node_id: NodeId) ->
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_number(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult {
|
|
||||||
let literal = src.ast.span(node_id).slice(src.code);
|
|
||||||
let float: f32 = literal
|
|
||||||
.parse()
|
|
||||||
.expect("the parser should've gotten us a string parsable by the stdlib");
|
|
||||||
|
|
||||||
c.chunk.emit_opcode(Opcode::Number)?;
|
|
||||||
c.chunk.emit_f32(float)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_tag(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult {
|
fn compile_tag(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult {
|
||||||
let tag = src.ast.span(node_id).slice(src.code);
|
let tag = src.ast.span(node_id).slice(src.code);
|
||||||
|
|
||||||
|
@ -230,6 +218,79 @@ fn compile_tag(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_number(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult {
|
||||||
|
let literal = src.ast.span(node_id).slice(src.code);
|
||||||
|
if let Ok(float) = literal.parse() {
|
||||||
|
c.chunk.emit_opcode(Opcode::Number)?;
|
||||||
|
c.chunk.emit_f32(float)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_color(c: &mut Compiler, src: &Source, node_id: NodeId) -> CompileResult {
|
||||||
|
let literal = src.ast.span(node_id).slice(src.code);
|
||||||
|
|
||||||
|
let hex = &literal[1..];
|
||||||
|
let bytes: [u8; 4] = u32::from_str_radix(hex, 16)
|
||||||
|
.ok()
|
||||||
|
.and_then(|num| match hex.len() {
|
||||||
|
3 => Some([
|
||||||
|
(((num & 0xF00) >> 8) * 0x11) as u8,
|
||||||
|
(((num & 0x0F0) >> 4) * 0x11) as u8,
|
||||||
|
((num & 0x00F) * 0x11) as u8,
|
||||||
|
0xFF,
|
||||||
|
]),
|
||||||
|
4 => Some([
|
||||||
|
(((num & 0xF000) >> 12) * 0x11) as u8,
|
||||||
|
(((num & 0x0F00) >> 8) * 0x11) as u8,
|
||||||
|
(((num & 0x00F0) >> 4) * 0x11) as u8,
|
||||||
|
((num & 0x000F) * 0x11) as u8,
|
||||||
|
]),
|
||||||
|
6 => Some([
|
||||||
|
((num & 0xFF0000) >> 16) as u8,
|
||||||
|
((num & 0x00FF00) >> 8) as u8,
|
||||||
|
(num & 0x0000FF) as u8,
|
||||||
|
0xFF,
|
||||||
|
]),
|
||||||
|
8 => Some([
|
||||||
|
((num & 0xFF000000) >> 24) as u8,
|
||||||
|
((num & 0x00FF0000) >> 16) as u8,
|
||||||
|
((num & 0x0000FF00) >> 8) as u8,
|
||||||
|
(num & 0x000000FF) as u8,
|
||||||
|
]),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or([0, 0, 0, 0]);
|
||||||
|
|
||||||
|
c.chunk.emit_opcode(Opcode::Rgba)?;
|
||||||
|
c.chunk.emit_bytes(&bytes)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_list<'a>(c: &mut Compiler<'a>, src: &Source<'a>, node_id: NodeId) -> CompileResult {
|
||||||
|
let mut walk = src.ast.walk(node_id);
|
||||||
|
|
||||||
|
let mut len = 0;
|
||||||
|
while let Some(expr) = walk.node() {
|
||||||
|
compile_expr(c, src, expr)?;
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = u16::try_from(len).unwrap_or_else(|_| {
|
||||||
|
// For all practical intents and purposes, this should never happen---you'll most likely
|
||||||
|
// run into the chunk length limit first.
|
||||||
|
c.emit(Diagnostic::error(src.ast.span(node_id), "list is too long"));
|
||||||
|
0
|
||||||
|
});
|
||||||
|
|
||||||
|
c.chunk.emit_opcode(Opcode::List)?;
|
||||||
|
c.chunk.emit_u16(len)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn compile_unary<'a>(c: &mut Compiler<'a>, src: &Source<'a>, node_id: NodeId) -> CompileResult {
|
fn compile_unary<'a>(c: &mut Compiler<'a>, src: &Source<'a>, node_id: NodeId) -> CompileResult {
|
||||||
let mut walk = src.ast.walk(node_id);
|
let mut walk = src.ast.walk(node_id);
|
||||||
let Some(op) = walk.node() else { return Ok(()) };
|
let Some(op) = walk.node() else { return Ok(()) };
|
||||||
|
|
|
@ -17,16 +17,16 @@ impl<'a> Lexer<'a> {
|
||||||
pub fn new(lexis: Lexis, input: &'a SourceCode) -> Self {
|
pub fn new(lexis: Lexis, input: &'a SourceCode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
lexis,
|
lexis,
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::with_capacity(16),
|
||||||
input,
|
input,
|
||||||
position: 0,
|
position: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current(&self) -> char {
|
fn current(&self) -> char {
|
||||||
self.input[self.position as usize..]
|
self.input
|
||||||
.chars()
|
.get(self.position as usize..)
|
||||||
.next()
|
.and_then(|s| s.chars().next())
|
||||||
.unwrap_or('\0')
|
.unwrap_or('\0')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ fn whitespace_and_comments(l: &mut Lexer<'_>) {
|
||||||
let position = l.position;
|
let position = l.position;
|
||||||
l.advance();
|
l.advance();
|
||||||
if l.current() == '-' {
|
if l.current() == '-' {
|
||||||
while l.current() != '\n' {
|
while l.current() != '\n' && l.current() != '\0' {
|
||||||
l.advance();
|
l.advance();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -143,7 +143,8 @@ pub mod fns {
|
||||||
0x88 Nary "rgbaB" => rgba_b,
|
0x88 Nary "rgbaB" => rgba_b,
|
||||||
0x89 Nary "rgbaA" => rgba_a,
|
0x89 Nary "rgbaA" => rgba_a,
|
||||||
|
|
||||||
0x90 Nary "list" => list,
|
// NOTE: Not used right now, has been replaced with Opcode::List.
|
||||||
|
0x90 Nary "list (unused)" => list,
|
||||||
|
|
||||||
0xc0 Nary "toShape" => to_shape_f,
|
0xc0 Nary "toShape" => to_shape_f,
|
||||||
0xc1 Nary "line" => line,
|
0xc1 Nary "line" => line,
|
||||||
|
|
|
@ -5,11 +5,12 @@ use core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloc::{string::String, vec::Vec};
|
use alloc::{string::String, vec::Vec};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bytecode::{self, Defs, Opcode, CAPTURE_CAPTURE, CAPTURE_LOCAL},
|
bytecode::{self, Defs, Opcode, CAPTURE_CAPTURE, CAPTURE_LOCAL},
|
||||||
system::{ChunkId, System},
|
system::{ChunkId, System},
|
||||||
value::{BytecodeLoc, Closure, FunctionName, Ref, RefId, Rgba, Value, Vec4},
|
value::{BytecodeLoc, Closure, FunctionName, List, Ref, RefId, Rgba, Value, Vec4},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct VmLimits {
|
pub struct VmLimits {
|
||||||
|
@ -208,6 +209,19 @@ impl Vm {
|
||||||
self.push(Value::Number(x))?;
|
self.push(Value::Number(x))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opcode::Rgba => {
|
||||||
|
let r = chunk.read_u8(&mut pc)?;
|
||||||
|
let g = chunk.read_u8(&mut pc)?;
|
||||||
|
let b = chunk.read_u8(&mut pc)?;
|
||||||
|
let a = chunk.read_u8(&mut pc)?;
|
||||||
|
self.push(Value::Rgba(Rgba {
|
||||||
|
r: r as f32 / 255.0,
|
||||||
|
g: g as f32 / 255.0,
|
||||||
|
b: b as f32 / 255.0,
|
||||||
|
a: a as f32 / 255.0,
|
||||||
|
}))?;
|
||||||
|
}
|
||||||
|
|
||||||
Opcode::Local => {
|
Opcode::Local => {
|
||||||
let index = chunk.read_u8(&mut pc)? as usize;
|
let index = chunk.read_u8(&mut pc)? as usize;
|
||||||
let value = self.get(bottom + index)?;
|
let value = self.get(bottom + index)?;
|
||||||
|
@ -246,6 +260,20 @@ impl Vm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opcode::List => {
|
||||||
|
let len = chunk.read_u16(&mut pc)? as usize;
|
||||||
|
let bottom = self.stack.len().checked_sub(len).ok_or_else(|| {
|
||||||
|
self.create_exception(
|
||||||
|
"corrupted bytecode (list has more elements than stack)",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let elements = self.stack[bottom..].to_vec();
|
||||||
|
self.stack.resize_with(bottom, || unreachable!());
|
||||||
|
self.track_array(&elements)?;
|
||||||
|
let id = self.create_ref(Ref::List(List { elements }))?;
|
||||||
|
self.push(Value::Ref(id))?;
|
||||||
|
}
|
||||||
|
|
||||||
Opcode::Function => {
|
Opcode::Function => {
|
||||||
let param_count = chunk.read_u8(&mut pc)?;
|
let param_count = chunk.read_u8(&mut pc)?;
|
||||||
let then = chunk.read_u16(&mut pc)? as usize;
|
let then = chunk.read_u16(&mut pc)? as usize;
|
||||||
|
|
507
docs/rkgk.dj
507
docs/rkgk.dj
|
@ -27,7 +27,7 @@ In case you edited anything in the input box on the right, paste the following t
|
||||||
-- Try playing around with the numbers,
|
-- Try playing around with the numbers,
|
||||||
-- and see what happens!
|
-- and see what happens!
|
||||||
|
|
||||||
stroke 8 (rgba 0 0 0 1) (vec 0 0)
|
stroke 8 #000 (vec 0 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
rakugaki is a drawing program for digital scribbles and other pieces of art.
|
rakugaki is a drawing program for digital scribbles and other pieces of art.
|
||||||
|
@ -78,36 +78,52 @@ Theoretically, this would mean brushes are very limited.
|
||||||
After all, if we're only limited to drawing single scribbles, wouldn't that mean a brush can only draw a single shape?
|
After all, if we're only limited to drawing single scribbles, wouldn't that mean a brush can only draw a single shape?
|
||||||
|
|
||||||
But the magical part is that you can _compose scribbles together._
|
But the magical part is that you can _compose scribbles together._
|
||||||
If you want to draw multiple scribbles, you can wrap them into a `list`:
|
If you want to draw multiple scribbles, you can wrap them into a list, which we denote with square brackets `[]`:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
; Draw two colorful dots instead of one!
|
-- Draw two colorful dots instead of one!
|
||||||
(list
|
[
|
||||||
(stroke 8 (rgba 1.0 0.0 0.0 1.0) (vec 4 0))
|
stroke 8 #F00 (vec 4 0)
|
||||||
(stroke 8 (rgba 0.0 0.0 1.0 1.0) (vec (- 4) 0)))
|
stroke 8 #00F (vec (-4) 0))
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
This is kind of weird, but to negate a number in haku, you have to write `(- number)`, with all the parentheses and spaces in place.
|
haku uses the syntax `-- OwO` for _comments_---human-readable pieces of text that are ignored by the compiler.
|
||||||
|
A comment begins with `--`, and ends at the end of a line.
|
||||||
|
|
||||||
You'll understand why later!
|
They're pretty useful for making your code more understandable!
|
||||||
|
After all, we don't speak programming languages natively.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
And what's even crazier is that you can composes lists _further_---you can make a list of lists, and rakugaki will be happy with that!
|
And what's even crazier is that you can compose lists _further_---you can make a list of lists, and rakugaki will be happy with that!
|
||||||
It'll draw the first inner list, which contains two scribbles, and then it'll draw the second inner list, which contains two scribbles.
|
It'll draw the first inner list, which contains two scribbles, and then it'll draw the second inner list, which contains two scribbles.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(list
|
[
|
||||||
(list
|
[
|
||||||
(stroke 8 (rgba 1.0 0.0 0.0 1.0) (vec 4 (- 4)))
|
stroke 8 #F00 (vec 4 (-4))
|
||||||
(stroke 8 (rgba 0.0 0.0 1.0 1.0) (vec (- 4) (- 4))))
|
stroke 8 #00F (vec (-4) (-4))
|
||||||
(list
|
]
|
||||||
(stroke 8 (rgba 1.0 1.0 0.0 1.0) (vec 4 4))
|
[
|
||||||
(stroke 8 (rgba 0.0 1.0 1.0 1.0) (vec (- 4) 4))))
|
stroke 8 #FF0 (vec 4 4)
|
||||||
|
stroke 8 #0FF (vec (-4) 4)
|
||||||
|
]
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::: aside
|
||||||
|
|
||||||
|
Another weird thing: when negating a number, you have to put 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.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
This might seem useless, but it's a really useful property in computer programs.
|
This might seem useless, but it's a really useful property in computer programs.
|
||||||
It essentially means you can snap pieces together like Lego bricks!
|
It essentially means you can snap pieces together like Lego bricks!
|
||||||
|
|
||||||
|
@ -120,122 +136,65 @@ Therefore, scribbles that are listed later will be drawn on top of scribbles tha
|
||||||
Anyways!
|
Anyways!
|
||||||
|
|
||||||
|
|
||||||
## So what's this ceremony with all the parentheses and such?
|
## So what's this ceremony with all the words and symbols?
|
||||||
|
|
||||||
::: aside
|
|
||||||
|
|
||||||
I'm working on an improved syntax for haku that will get rid of the prevalence of parentheses.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
Recall that super simple brush from before...
|
Recall that super simple brush from before...
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(stroke
|
stroke 8 #000 (vec 0 0)
|
||||||
8
|
|
||||||
(rgba 0.0 0.0 0.0 1.0)
|
|
||||||
(vec))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It'll be best to explain what happens here by example, so let's have a look at that singular `rgba` line.
|
This reads as "a stroke that's 8 pixels wide, has the color `#000`, and is drawn at the point `(0, 0)` relative to the mouse cursor."
|
||||||
|
|
||||||
```haku
|
All these symbols are very meaningful to haku.
|
||||||
(rgba 0.0 0.0 0.0 1.0)
|
If you reorder or remove any one of them, your brush isn't going to work!
|
||||||
```
|
|
||||||
|
|
||||||
This is the syntax haku uses for representing RGBA colors.
|
- Reading from left to right, we start with `stroke`.\
|
||||||
|
`stroke` is a _function_---a recipe for producing data!\
|
||||||
|
haku has [many such built-in recipes](/docs/system.html).
|
||||||
|
`stroke` is one of them.
|
||||||
|
|
||||||
It looks simple enough, but we can dismantle it even further.
|
- Each function requires some amount of _arguments_.
|
||||||
|
These are the ingredients that will be used to produce our piece of data.\
|
||||||
|
In haku, we specify the arguments to a function by listing them on the same line as the function's name, one after another, separated by spaces.
|
||||||
|
|
||||||
- There's the word `rgba`.
|
- The first ingredient we need for a `stroke` is its _thickness_.
|
||||||
|
This is a plain old number, counted in pixels. We say we want a stroke of thickness `8`.
|
||||||
|
|
||||||
- There are four number values, `0.0`, `0.0`, `0.0`, and `1.0`.
|
- The second ingredient is the stroke's _color_.
|
||||||
|
haku uses the familiar hex code syntax `#RRGGBB` for colors, but it allows writing `#RGB` for brevity---`#08F` is the same as `#0088FF`.\
|
||||||
|
You can also specify an alpha channel, for transparent colors---`#RRGGBBAA`, or `#RGBA`.
|
||||||
|
|
||||||
- All of this is wrapped in parentheses.
|
- The third ingredient is the stroke's _position_.
|
||||||
|
|
||||||
[*The parentheses are _very_ significant.*]{id=what-happens-if-you-remove-the-parentheses}
|
Positions in haku are represented using mathematical _vectors_, which, when broken down into pieces, are just lists of some numbers.
|
||||||
If we remove them, our brush no longer runs, and instead fails with an error.
|
|
||||||
|
|
||||||
```haku
|
|
||||||
(stroke
|
|
||||||
8
|
|
||||||
rgba 0.0 0.0 0.0 1.0
|
|
||||||
(vec))
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
14..18: undefined variable
|
|
||||||
```
|
|
||||||
|
|
||||||
So what's happening here?
|
|
||||||
|
|
||||||
Put shortly, the parentheses are telling haku to _call a function._
|
|
||||||
This is computer-speak for asking haku to _do_ something with data.
|
|
||||||
|
|
||||||
In case of `(rgba 0.0 0.0 0.0 1.0)`, we're telling haku to create an RGBA color out of four numbers, which signify the Red, Green, Blue, and Alpha channels of our color.
|
|
||||||
|
|
||||||
In computer-speak, `rgba` is a _function_, which we _call_ with _four number arguments_, and it _returns_ an RGBA color for us to use.
|
|
||||||
|
|
||||||
If you've studied math in school, you can visualize haku functions as being very similar to mathematical functions.
|
|
||||||
Both take in arguments, and both can be substituted for some results.
|
|
||||||
Both also have domains---just as it's invalid to take the square root of a negative number, it is invalid to call `rgba` with something other than four numbers.
|
|
||||||
|
|
||||||
haku defines many such ready-to-use functions.
|
|
||||||
These functions make up the _system library_---just like in a real world library there are lots of books, the haku system library has lots of functions.
|
|
||||||
We call it the _system_ library, because together with the programming language, they make up a system of rules, which we collectively call _haku_.
|
|
||||||
|
|
||||||
Philosophy aside, you can reference all the functions from the system library in the [system library reference](/docs/system.html).
|
|
||||||
|
|
||||||
Anyways, to finish off dismantling that entire `stroke` example---
|
|
||||||
|
|
||||||
```haku
|
|
||||||
(stroke
|
|
||||||
8
|
|
||||||
(rgba 0.0 0.0 0.0 1.0)
|
|
||||||
(vec))
|
|
||||||
```
|
|
||||||
|
|
||||||
Just like `rgba`, `vec` is also a system function.
|
|
||||||
|
|
||||||
It's a function which produces a mathematical _vector_, which, in simple terms, is just a list of some numbers.
|
|
||||||
|
|
||||||
haku vectors however are a little more constrained, because they always contain _four_ numbers---this makes them _four-dimensional_.
|
haku vectors however are a little more constrained, because they always contain _four_ numbers---this makes them _four-dimensional_.
|
||||||
We call these four numbers X, Y, Z, and W respectively.
|
We call these four numbers X, Y, Z, and W respectively.
|
||||||
|
|
||||||
Four is a useful number of dimensions to have, because it lets us do 3D math---which technically isn't built into haku, but if you want it, it's there.
|
Four is a useful number of dimensions to have, because it lets us do 3D math---which technically isn't built into haku, but if you want it, it's there.
|
||||||
|
|
||||||
For most practical purposes, we'll only be using the first _two_ of the four dimensions though---X and Y.
|
For most practical purposes, we'll only be using the first _two_ of the four dimensions though---X and Y.
|
||||||
This is because the wall is a 2D space---it's a flat surface with no depth.
|
This is because the wall is a 2D space---it's a flat surface with no depth.
|
||||||
|
|
||||||
We most commonly use vectors to represent points on the wall.
|
It's important to know though that vectors don't mean much _by themselves_---rakugaki just chooses them to represent points on the wall, but in a flat 2D space, all points need to be relative to some _origin_---the vector `(0, 0)`.
|
||||||
These points are always relative to some origin, which is located at the coordinates `(0, 0)`.
|
In brushes, this position is at the tip of the mouse cursor.
|
||||||
In case of brushes, we consider the origin to be the located at the tip of the mouse cursor.
|
|
||||||
|
|
||||||
Positive X coordinates go rightwards, and positive Y coordinates go downwards.
|
Positive X coordinates go rightwards, and positive Y coordinates go downwards.
|
||||||
Likewise, negative X coordinates go leftwards, and negative Y coordinates go upwards.
|
Likewise, negative X coordinates go leftwards, and negative Y coordinates go upwards.
|
||||||
|
|
||||||
::: aside
|
---
|
||||||
|
|
||||||
Many programming languages (and math libraries) make points a different type of data from vectors.
|
Going back to the example though, `vec` is yet another function, except instead of producing strokes, it produces vectors!
|
||||||
|
|
||||||
The reason for this is that it doesn't make sense to, for example, add points together.
|
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!
|
||||||
You can only add a vector to a point, which gives you a point, or a vector to a vector, which gives you a vector, but not a point to a point---because that operation simply doesn't make sense mathematically.
|
|
||||||
|
|
||||||
haku goes with the simpler way of using one data type, because I've never found this to be useful in practice.
|
And with all that, we let haku mix all the ingredients together, and get a black dot under the cursor.
|
||||||
In theory it can prevent bugs, but usually these bugs are easy enough to see and squash immediately.
|
|
||||||
|
|
||||||
:::
|
```haku
|
||||||
|
stroke 8 #000 (vec 0 0)
|
||||||
|
```
|
||||||
|
|
||||||
`(vec)` is a short way of saying, "a vector with all dimensions equal to zero."
|
Nice!
|
||||||
We could just as well write `(vec 0 0 0 0)`, but that's no fun to type every single time you'd like a zero vector.
|
|
||||||
Therefore, in case of `vec`, haku allows you to omit any of the four arguments, and initializes them to zero for you.
|
|
||||||
|
|
||||||
And lastly, there's the `stroke` function.
|
|
||||||
It turns a stroke thickness (number,) color (`rgba`,) and position (vector,) into a scribble that we can draw on the wall.
|
|
||||||
|
|
||||||
Just like cooking!
|
|
||||||
You add eggs, milk, flour, some salt, and some sugar, mix them up, and you get pancake batter.
|
|
||||||
In haku, you add thickness, color, and position, mix them together into a `stroke`, and get a little colored square on the wall!
|
|
||||||
|
|
||||||
|
|
||||||
## Shapes
|
## Shapes
|
||||||
|
@ -250,15 +209,10 @@ Right now haku supports two additional shapes: rectangles and circles.
|
||||||
You can try them out by playing with this brush!
|
You can try them out by playing with this brush!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(list
|
[
|
||||||
(stroke
|
stroke 8 #F00 (circle (-16) 0 16)
|
||||||
8
|
stroke 8 #00F (rect 0 (-16) 32 32)
|
||||||
(rgba 1.0 0.0 0.0 1.0)
|
]
|
||||||
(circle (- 16) 0 16))
|
|
||||||
(stroke
|
|
||||||
8
|
|
||||||
(rgba 0.0 0.0 1.0 1.0)
|
|
||||||
(rect 0 (- 16) 32 32)))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
@ -272,7 +226,7 @@ But let's not go down that rabbit hole.
|
||||||
- `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).\
|
||||||
Our example produces a square, because the rectangle's width and height are the same!
|
Our example produces a square, because the rectangle's width and height are equal!
|
||||||
|
|
||||||
## Programming in haku
|
## Programming in haku
|
||||||
|
|
||||||
|
@ -282,19 +236,21 @@ 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
|
||||||
(list
|
[
|
||||||
(stroke 8 (rgba 1.0 0.0 0.0 1.0) (vec 4 0))
|
stroke 8 #F00 (vec 4 0)
|
||||||
(stroke 8 (rgba 0.0 0.0 1.0 1.0) (vec (- 4) 0)))
|
stroke 8 #00F (vec (-4) 0)
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
It has quite a bit of repetition in it.
|
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
|
||||||
(list
|
[
|
||||||
; ↓
|
stroke 4 #F00 (vec 4 0)
|
||||||
(stroke 4 (rgba 1.0 0.0 0.0 1.0) (vec 4 0))
|
stroke 4 #00F (vec (-4) 0)
|
||||||
(stroke 4 (rgba 0.0 0.0 1.0 1.0) (vec (- 4) 0)))
|
---
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
...twice of course, because we have two scribbles.
|
...twice of course, because we have two scribbles.
|
||||||
|
@ -302,17 +258,19 @@ But now there's a gap between our points!
|
||||||
So we also have to update their positions.
|
So we also have to update their positions.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(list
|
[
|
||||||
; ↓
|
stroke 4 #F00 (vec 2 0)
|
||||||
(stroke 4 (rgba 1.0 0.0 0.0 1.0) (vec 2 0))
|
---
|
||||||
; ↓
|
stroke 4 #00F (vec (-2) 0)
|
||||||
(stroke 4 (rgba 0.0 0.0 1.0 1.0) (vec (- 2) 0)))
|
--
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Now imagine if we had four of those points.
|
Now imagine if we had four of those points.
|
||||||
That's quite a lot of copy-pasting for such a simple thing!
|
That's quite a lot of copy-pasting for such a simple thing!
|
||||||
|
|
||||||
Luckily, haku has a solution for this: we can give a _name_ to a piece of data by using a _definition_, and then refer to that piece of data using that name we chose.
|
Luckily, haku has a solution for this: we can give a _name_ to a piece of data by using a _definition_, and then refer to that piece of data using that name we chose.
|
||||||
|
Definitions are called _defs_ in short.
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
|
@ -326,48 +284,47 @@ Once you define a name, its associated data stays the same throughout the entire
|
||||||
So we can define `thickness` to be `4`, and then use it in our scribbles.
|
So we can define `thickness` to be `4`, and then use it in our scribbles.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def thickness 4)
|
thickness = 4
|
||||||
|
|
||||||
(list
|
[
|
||||||
(stroke thickness (rgba 1.0 0.0 0.0 1.0) (vec 2 0))
|
stroke thickness #F00 (vec 2 0)
|
||||||
(stroke thickness (rgba 0.0 0.0 1.0 1.0) (vec (- 2) 0)))
|
stroke thickness #00F (vec (-2) 0)
|
||||||
; ^^^^^^^^^
|
---------
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
`(def name data)` is a special construction in haku that tells the language "whenever we say `name`, we mean `data`."
|
`name = data` is a special operator in haku that tells the language "whenever we say `name`, we mean `data`."
|
||||||
Unlike `list` or `stroke`, it is _not_ a function!
|
|
||||||
|
|
||||||
We cannot put `def`s in arbitrary places in our program, because it wouldn't make sense.
|
We cannot use it in arbitrary places in our program, because it wouldn't make sense.
|
||||||
What does it mean to have a stroke whose thickness is `(def meow 5)`?
|
What does it mean to have a stroke whose thickness is `meow = 5`?
|
||||||
|
|
||||||
To keep a consistent program structure, haku also forces all your `def`s to appear _before_ your scribble.
|
To keep a consistent program structure, haku also forces all your defs to appear _before_ your scribble.
|
||||||
You can think of the `def`s 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!
|
||||||
|
|
||||||
Anyways, we can likewise replace our `2` constants with a `def`:
|
Anyways, we can likewise replace our `2` constants with a def:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def thickness 4)
|
thickness = 4
|
||||||
(def x-offset 2)
|
xOffset = 2
|
||||||
|
|
||||||
(list
|
[
|
||||||
(stroke thickness (rgba 1.0 0.0 0.0 1.0) (vec x-offset 0))
|
stroke thickness #F00 (vec xOffset 0)
|
||||||
(stroke thickness (rgba 0.0 0.0 1.0 1.0) (vec (- x-offset) 0)))
|
stroke thickness #00F (vec (-xOffset) 0)
|
||||||
; ^^^^^^^^^
|
---------
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
I chose the name `x-offset` because it demonstrates that names don't need to be made up of letters only.
|
Note how in haku, names may not contain spaces.
|
||||||
In fact, haku will be happy with any of the following characters:
|
We cannot have a variable called `x offset`, so we choose `xOffset` instead.
|
||||||
|
This naming convention is known as `camelCase`, and is used everywhere throughout the haku system library.
|
||||||
- lowercase and uppercase letters
|
|
||||||
- numbers (as long as your name doesn't start with a number)
|
|
||||||
- dashes `-` and underscores `_`
|
|
||||||
- various symbols: `+`, `*`, `/`, `\`, `^`, `!`, `=`, `<`, `>`---these are mainly used in math functions like `+`, but nothing prevents you from using them yourself!
|
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
Fancy symbols in names are typical of languages in the [Lisp](https://en.wikipedia.org/wiki/Lisp_%28programming_language%29) family.
|
Of note is that haku names also cannot start with an uppercase letter.
|
||||||
After all, since there are so few syntactic constructions---only literal values and lists---why not make the rest of the characters on your keyboard available for naming things?
|
It's reserved syntax for the future.
|
||||||
|
|
||||||
|
Right now the only names that start with an uppercase letter are the two booleans, `True` and `False`.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@ -375,24 +332,27 @@ But now there's a problem.
|
||||||
If we change our `thickness` back to `8`, our points will overlap!
|
If we change our `thickness` back to `8`, our points will overlap!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
; ↓
|
thickness = 8
|
||||||
(def thickness 8)
|
---
|
||||||
(def x-offset 2)
|
xOffset = 2
|
||||||
|
|
||||||
(list
|
[
|
||||||
(stroke thickness (rgba 1.0 0.0 0.0 1.0) (vec x-offset 0))
|
stroke thickness #F00 (vec xOffset 0)
|
||||||
(stroke thickness (rgba 0.0 0.0 1.0 1.0) (vec (- x-offset) 0)))
|
stroke thickness #00F (vec (-xOffset) 0)
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
So we'll make our `x-offset` calculated dynamically from the `thickness`, to not have to update it every time.
|
So we'll make our `xOffset` calculated dynamically from the `thickness`, to not have to update it every time.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def thickness 8)
|
thickness = 8
|
||||||
(def x-offset (/ thickness 2))
|
xOffset = thickness / 2
|
||||||
|
-------------
|
||||||
|
|
||||||
(list
|
[
|
||||||
(stroke thickness (rgba 1.0 0.0 0.0 1.0) (vec x-offset 0))
|
stroke thickness #F00 (vec xOffset 0)
|
||||||
(stroke thickness (rgba 0.0 0.0 1.0 1.0) (vec (- x-offset) 0)))
|
stroke thickness #00F (vec (-xOffset) 0)
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Try playing with the `thickness` now!
|
Try playing with the `thickness` now!
|
||||||
|
@ -404,18 +364,14 @@ 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
|
fill #000 (circle 0 0 16)
|
||||||
(rgba 0 0 0 1)
|
|
||||||
(circle 0 0 16))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
How about... some transparency?
|
How about... some transparency?
|
||||||
Recall that `rgba` has a fourth Alpha parameter---in image manipulation, this parameter is used to control the _opacity_ of a color.
|
Recall that colors can have an alpha component, so let's try using that!
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(fill
|
fill #0001 (circle 0 0 16)
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 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.
|
||||||
|
@ -424,42 +380,32 @@ 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
|
||||||
(list
|
[
|
||||||
(fill
|
fill #0001 (circle 0 0 16)
|
||||||
(rgba 0 0 0 0.1)
|
fill #0001 (circle 0 0 32)
|
||||||
(circle 0 0 16))
|
]
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 32)))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
How about four?
|
How about four?
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(list
|
[
|
||||||
(fill
|
fill #0001 (circle 0 0 8)
|
||||||
(rgba 0 0 0 0.1)
|
fill #0001 (circle 0 0 16)
|
||||||
(circle 0 0 8))
|
fill #0001 (circle 0 0 24)
|
||||||
(fill
|
fill #0001 (circle 0 0 32)
|
||||||
(rgba 0 0 0 0.1)
|
]
|
||||||
(circle 0 0 16))
|
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 24))
|
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 32)))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Okay, this is starting to look interesting, but it's also getting super unwieldy code-wise!
|
Okay, this is starting to look interesting, but it's also getting super unwieldy code-wise!
|
||||||
I mean, just look at these repeated lines...
|
I mean, just look at these repeated lines...
|
||||||
Doesn't that remind you of that previous code example?
|
Doesn't that remind you of that previous code example?
|
||||||
Could there be some way to cleverly use `def`s to make it more readable?
|
Could there be some way to cleverly use defs to make it more readable?
|
||||||
|
|
||||||
...Well, the problem here's that the values vary, while `def`s are constant!
|
...Well, the problem here's that the values vary, while defs are constant!
|
||||||
So no `def` in the world is going to save us here.
|
So no def in the world is going to save us here.
|
||||||
|
|
||||||
But what if we could `def` some _code_, and then weave our changing values into that?
|
But what if we could def some _code_, and then weave our changing values into that?
|
||||||
Or, maybe in other words, list a bunch of values, and then transform them into something else?
|
Or, maybe in other words, list a bunch of values, and then transform them into something else?
|
||||||
|
|
||||||
...We already have a tool for that!
|
...We already have a tool for that!
|
||||||
|
@ -469,17 +415,14 @@ Or, maybe in other words, list a bunch of values, and then transform them into s
|
||||||
Just like haku defines a set of _system_ functions, we can create and define _our own_ functions too!
|
Just like haku defines a set of _system_ functions, we can create and define _our own_ functions too!
|
||||||
|
|
||||||
In haku, functions are data like anything else.
|
In haku, functions are data like anything else.
|
||||||
We create them using `fn`, which is yet another magical construct like `def`.
|
We create them using the syntax `\x -> y`.
|
||||||
Except because functions are real data, they're not as restricted as `def`s are---they behave like any other piece of data, such as numbers.
|
Because they are data like anything else, we can give them names with defs, or we can pass them into other functions for further manipulation.
|
||||||
We can give them names with `def`s, or we can pass them into other functions for further manipulation.
|
|
||||||
|
|
||||||
::: aside
|
::: aside
|
||||||
|
|
||||||
Actually, system functions are kind of special.
|
Actually, system functions are kind of special.
|
||||||
For performance reasons, (and because I was hasty to get a working prototype,) they cannot be passed as arguments to other functions.
|
For performance reasons, (and because I was hasty to get a working prototype,) they cannot be passed as arguments to other functions.
|
||||||
|
|
||||||
It's why [the error message here is so cryptic](#what-happens-if-you-remove-the-parentheses).
|
|
||||||
|
|
||||||
That'll need fixing!
|
That'll need fixing!
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
@ -487,34 +430,33 @@ 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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #0001 (circle 0 0 radius)
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(list
|
[
|
||||||
(splat 8)
|
splat 8
|
||||||
(splat 16)
|
splat 16
|
||||||
(splat 24)
|
splat 24
|
||||||
(splat 32))
|
splat 32
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
That's a lot nicer, isn't it---a template for our circles is neatly defined in a single place, and all we do is reuse it, each time with a different `radius`.
|
That's a lot nicer, isn't it---a template for our circles is neatly defined in a single place, and all we do is reuse it, each time with a different `radius`.
|
||||||
|
|
||||||
To dismantle that `fn` literal...
|
To dismantle that weird `\` syntax...
|
||||||
|
|
||||||
- We have the special word `fn`, which is short for _*f*u*n*ction_.
|
- The character `\` is a short way of saying _function of_.
|
||||||
|
It's supposed to resemble the Greek letter λ, but be easier to type on our antiquated ASCII keyboards.
|
||||||
|
|
||||||
- We have a list of _parameters_.
|
- After `\`, we have a list of _parameters_.
|
||||||
|
|
||||||
Parameters are the names we give to a function's arguments---for a function call `(splat 8)`, we need the function to have a name for that `8` datum that gets passed to it.
|
Parameters are the names we give to a function's arguments---for a function call `splat 8`, we need the function to have a name for that `8` datum that gets passed to it.
|
||||||
Otherwise it has no way to use it!
|
Otherwise it has no way to use it!
|
||||||
|
|
||||||
A function can have an arbitrary number of parameters listed, 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!
|
||||||
|
|
||||||
- And lastly, 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.
|
||||||
|
|
||||||
|
@ -533,21 +475,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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #0001 (circle 0 0 radius)
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(list
|
[
|
||||||
(splat 8)
|
splat 8
|
||||||
(splat 16)
|
splat 16
|
||||||
(splat 24)
|
splat 24
|
||||||
(splat 32)
|
splat 32
|
||||||
(splat 40)
|
splat 40
|
||||||
(splat 48)
|
splat 48
|
||||||
(splat 56)
|
splat 56
|
||||||
(splat 64))
|
splat 64
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Okay, I'll admit this is getting kind of dumb.
|
Okay, I'll admit this is getting kind of dumb.
|
||||||
|
@ -580,19 +520,16 @@ 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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #0001 (circle 0 0 radius)
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(def airbrush
|
airbrush = \size ->
|
||||||
(fn (size)
|
[
|
||||||
(list
|
splat size
|
||||||
(splat size)
|
airbrush (size - 8)
|
||||||
(airbrush (- size 8)))))
|
]
|
||||||
|
|
||||||
(airbrush 64) ; sounds like some Nintendo 64 game about graffiti, lol.
|
airbrush 64 -- sounds like some Nintendo 64 game about graffiti, lol.
|
||||||
```
|
```
|
||||||
|
|
||||||
But...
|
But...
|
||||||
|
@ -618,13 +555,15 @@ We call this act of switching execution paths _branching_.
|
||||||
Try this out---change the `radius`, and observe how your brush changes color once you set it beyond 16:
|
Try this out---change the `radius`, and observe how your brush changes color once you set it beyond 16:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def radius 8)
|
radius = 8
|
||||||
|
|
||||||
(fill
|
color =
|
||||||
(if (< radius 16)
|
if (radius < 16)
|
||||||
(rgba 0 0 1 1)
|
#00F
|
||||||
(rgba 1 0 0 1))
|
else
|
||||||
(circle 0 0 radius))
|
#F00
|
||||||
|
|
||||||
|
fill color (circle 0 0 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.
|
||||||
|
@ -639,21 +578,19 @@ 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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #0001 (circle 0 0 radius)
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(def airbrush
|
airbrush = \size ->
|
||||||
(fn (size)
|
if (size > 0)
|
||||||
(if (> size 0)
|
[
|
||||||
(list
|
splat size
|
||||||
(splat size)
|
airbrush (size - 8)
|
||||||
(airbrush (- size 8)))
|
]
|
||||||
(list))))
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
(airbrush 64)
|
airbrush 64
|
||||||
```
|
```
|
||||||
|
|
||||||
Neat!
|
Neat!
|
||||||
|
@ -666,43 +603,39 @@ 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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #0001 (circle 0 0 radius)
|
||||||
(fill
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(def airbrush
|
airbrush = \size ->
|
||||||
(fn (size)
|
if (size > 0)
|
||||||
(if (> size 0)
|
[
|
||||||
(list
|
splat size
|
||||||
(splat size)
|
airbrush (size - 1)
|
||||||
; ↓
|
---
|
||||||
(airbrush (- size 1)))
|
]
|
||||||
(list))))
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
(airbrush 64)
|
airbrush 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
|
||||||
(def splat
|
splat = \radius ->
|
||||||
(fn (radius)
|
fill #00000004 (circle 0 0 radius)
|
||||||
(fill
|
---------
|
||||||
; ↓
|
|
||||||
(rgba 0 0 0 0.01)
|
|
||||||
(circle 0 0 radius))))
|
|
||||||
|
|
||||||
(def airbrush
|
airbrush = \size ->
|
||||||
(fn (size)
|
if (size > 0)
|
||||||
(if (> size 0)
|
[
|
||||||
(list
|
splat size
|
||||||
(splat size)
|
airbrush (size - 1)
|
||||||
(airbrush (- size 1)))
|
]
|
||||||
(list))))
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
(airbrush 64)
|
airbrush 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??
|
||||||
|
|
419
docs/system.dj
419
docs/system.dj
|
@ -9,230 +9,243 @@ Each function comes with a _signature description_.
|
||||||
These descriptions read like this:
|
These descriptions read like this:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(stroke
|
stroke
|
||||||
(thickness number)
|
thickness : number
|
||||||
(color rgba)
|
color : rgba
|
||||||
(position vec)
|
position : vec
|
||||||
scribble)
|
-> scribble
|
||||||
```
|
```
|
||||||
|
|
||||||
The first element is always the function's name - in this case `stroke`.
|
The first element is always the function's name - in this case `stroke`.
|
||||||
Following this are `(argument-name argument-type)` pairs, which describe the arguments that need to be passed to the function.
|
Following this are `argumentName : argumentType` pairs, which describe the arguments that need to be passed to the function.
|
||||||
The last element is always the type of data this function produces.
|
The last element is always the type of data this function produces.
|
||||||
|
|
||||||
|
Function names which are made up of symbols instead of letters are _operators_.
|
||||||
|
Operators may have one or two arguments, where one argument corresponds to a prefix form `-x`, and two arguments correspond to an infix form `x - y`.
|
||||||
|
Note that this documentation lists a unary and binary operator of the same spelling as _two separate functions_, not overloads of a single function.
|
||||||
|
|
||||||
The argument name usually does not matter when calling the function - it is only used for documentation purposes.
|
The argument name usually does not matter when calling the function - it is only used for documentation purposes.
|
||||||
The one exception is arguments called `...`, which signify that zero or more arguments can be passed to the function at that position.
|
The one exception is arguments called `...`, which signify that zero or more arguments can be passed to the function at that position.
|
||||||
|
(Currently there are no functions that accept any number of arguments, though.)
|
||||||
|
|
||||||
The argument _type_ however is important.
|
The argument _type_ however is important.
|
||||||
If you try to use a function with the wrong type of value as its argument, it will fail with an error.
|
If you try to use a function with the wrong type of value as its argument, it will fail with an error.
|
||||||
For example, consider a brush where we pass a number as `stroke`'s `color` and `position` arguments.
|
For example, consider a brush where we pass a number as `stroke`'s `color` and `position` arguments.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(stroke 1 1 1)
|
stroke 1 1 1
|
||||||
```
|
```
|
||||||
|
|
||||||
This brush will fail to render, since `stroke` expects an `rgba` as its 2nd argument.
|
This brush will fail to render, since `stroke` expects an `rgba` as its 2nd argument.
|
||||||
|
|
||||||
With that said, there are several types of values in haku that can be passed into, and returned by functions.
|
With that said, there are several types of values in haku that can be passed into, and returned by functions.
|
||||||
|
|
||||||
- `*` - special type used to signify that any value may be passed into the function.
|
- `_` - special type used to signify that any value may be passed into the function.
|
||||||
- `()` - also known as _nil_, means _no value._
|
- `()` - also known as _nil_, means _no value._
|
||||||
- `boolean` - either `false` or `true`. Indicates truth or falsehood, used in `if` conditions.
|
- `boolean` - either `False` or `True`. Indicates truth or falsehood, used in `if` conditions.
|
||||||
- `number` - a real number, with 32 bits of precision.
|
- `number` - a real number, with 32 bits of precision.
|
||||||
- `vec` - a 4-dimensional vector, composed of four `number`s.
|
- `vec` - a 4-dimensional vector, composed of four `number`s.
|
||||||
- `rgba` - an RGBA color, composed of four `number`s.
|
- `rgba` - an RGBA color, composed of four `number`s.
|
||||||
- `fn` - a function, as returned by `(fn (x) x)` literals.
|
- `\a -> r` - a function taking in the parameter `a` and returning `r`, as returned by `\x -> x` literals.
|
||||||
- `list` - a list of values, where each value can have a different type (even `list` itself.)
|
- `list t` - a list of values, where each value is of the type `t`.
|
||||||
- `shape` - a mathematical shape.
|
- `shape` - a mathematical shape.
|
||||||
|
|
||||||
- `shape-like` - anything that can be turned into a `shape` using `to-shape`.
|
- `shapeLike` - anything that can be turned into a `shape` using `toShape`.
|
||||||
|
|
||||||
- `scribble` - something that can be drawn on the wall.
|
- `scribble` - something that can be drawn on the wall.
|
||||||
|
|
||||||
Additionally, the syntax `(type-a type-b ...)` may be used to signify that one of the listed types is accepted or returned.
|
Additionally, the syntax `a | b` may be used to signify that one of the listed types is accepted or returned.
|
||||||
|
|
||||||
|
|
||||||
## Math
|
## Math
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(+
|
-
|
||||||
(... number)
|
a : number
|
||||||
number)
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
`+` takes an arbitrary amount of arguments and sums them together.
|
`-`, when used in its unary form `-x`, returns the number `x` with the opposite sign.
|
||||||
When there are zero arguments, it returns `0`.
|
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(-
|
+
|
||||||
(a number)
|
a : number
|
||||||
(... number)
|
b : number
|
||||||
number)
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
When there is only one argument, `-` returns `a` with the opposite sign.
|
`+` adds two numbers together.
|
||||||
Otherwise, it performs an arbitrary amount of subtractions from `a`, and returns the result.
|
|
||||||
Note that unlike `+` and `*`, at least one argument must be present.
|
|
||||||
|
|
||||||
haku does not have syntactic support for negative numbers - the proper way to negate a number is using this function `(- 1)`.
|
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(*
|
-
|
||||||
(... number)
|
a : number
|
||||||
number)
|
b : number
|
||||||
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
`*` takes an arbitrary amount of arguments and multiplies them together.
|
`-`, when used in its binary form `x - y`, subtracts two numbers from one another.
|
||||||
When there are zero arguments, it returns `1`.
|
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(/
|
*
|
||||||
(a number)
|
a : number
|
||||||
(... number)
|
b : number
|
||||||
number)
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
`/` returns `a` divided by all the numbers from `...`.
|
`*` multiplies two numbers together.
|
||||||
Note that unlike `+` and `*`, at least one argument must be present.
|
|
||||||
|
```haku
|
||||||
|
/
|
||||||
|
a : number
|
||||||
|
b : number
|
||||||
|
-> number
|
||||||
|
```
|
||||||
|
|
||||||
|
`/` divides a number by another number.
|
||||||
|
|
||||||
|
|
||||||
## Logic
|
## Logic
|
||||||
|
|
||||||
The following functions are used to compare values and work with `boolean`s.
|
The following functions are used to compare values and work with `boolean`s.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(not
|
!
|
||||||
(b *)
|
a : _
|
||||||
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
|
||||||
(=
|
==
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
|
|
||||||
(<>
|
!=
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
```
|
```
|
||||||
|
|
||||||
`=` returns `true` if `a` and `b` are equal.
|
`==` returns `True` if `a` and `b` are equal.
|
||||||
Whether two values are considered equal depends on their type:
|
Whether two values are considered equal depends on their type:
|
||||||
|
|
||||||
- If the type of the two values differs, `false` is returned.
|
- If the type of the two values differs, `False` is returned.
|
||||||
- If the two values are `number`s:
|
- If the two values are `number`s:
|
||||||
|
|
||||||
- If any of the values are `NaN`, `false` is returned.
|
- If any of the values are `NaN`, `False` is returned.
|
||||||
- Otherwise `true` is returned if the two numbers have the exact same bit representation.
|
- Otherwise `True` is returned if the two numbers have the exact same bit representation.
|
||||||
|
|
||||||
- If the two values are `vec`s, `true` is returned if each of their `number` components is equal to each other using the rules above.
|
- If the two values are `vec`s, `True` is returned if each of their `number` components is equal to each other using the rules above.
|
||||||
- Likewise with `rgba`s.
|
- Likewise with `rgba`s.
|
||||||
- All other types of values use _reference_ equality - `true` is returned only if `a` and `b` are located in the same place in memory.
|
- All other types of values use _reference_ equality - `True` is returned only if `a` and `b` are located in the same place in memory.
|
||||||
This more or less means that the values are considered equal if they are produced by the same call to a system function, in time.
|
This more or less means that the values are considered equal if they are produced by the same call to a system function, in time.
|
||||||
|
|
||||||
`<>` returns `(not (= a b))`.
|
`!=` returns `!(a == b)`.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(<
|
<
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
|
|
||||||
(<=
|
<=
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
|
|
||||||
(>
|
>
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
|
|
||||||
(>=
|
>=
|
||||||
(a *)
|
a : _
|
||||||
(b *)
|
b : _
|
||||||
boolean)
|
-> boolean
|
||||||
```
|
```
|
||||||
|
|
||||||
`<` returns `true` if `a` is less than `b`, and `<=` returns `true` if `a` is less than _or_ equal to `b`.
|
`<` returns `True` if `a` is less than `b`, and `<=` returns `True` if `a` is less than _or_ equal to `b`.
|
||||||
|
|
||||||
Order is only well-defined for numbers.
|
Order is only well-defined for numbers.
|
||||||
Other types may assume an arbitrary but consistent ordering - `()` may be less than `true`, or it may not be less than `true`, but this will not change between executions of the program.
|
Other types may assume an arbitrary but consistent ordering - `()` may be less than `True`, or it may not be less than `True`, but this will not change between executions of the program.
|
||||||
|
|
||||||
`(> a b)` is the same as `(< b a)`.
|
`a > b` is the same as `b < a`.
|
||||||
`(>= a b)` is the same as `(<= b a)`.
|
`a >= b` is the same as `b <= a`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Note that `and` and `or` are currently missing from this list.
|
Note that `and` and `or` are currently missing from this list, but are reserved keywords.
|
||||||
You can implement them using regular functions as a replacement.
|
You can implement them using regular functions as a replacement.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def and
|
boolAnd = \a, b ->
|
||||||
(fn (a b)
|
if (a)
|
||||||
(if a (if b true false) false)))
|
if (b) True
|
||||||
|
else False
|
||||||
|
else False
|
||||||
|
|
||||||
(def or
|
boolOr = \a, b ->
|
||||||
(fn (a b)
|
if (a)
|
||||||
(if a true (if b true false))))
|
True
|
||||||
|
else
|
||||||
|
if (b) True
|
||||||
|
else False
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Vectors
|
## Vectors
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(vec
|
vec
|
||||||
vec)
|
x : number
|
||||||
|
-> vec
|
||||||
|
|
||||||
(vec
|
vec
|
||||||
(x number)
|
x : number
|
||||||
vec)
|
y : number
|
||||||
|
-> vec
|
||||||
|
|
||||||
(vec
|
vec
|
||||||
(x number)
|
x : number
|
||||||
(y number)
|
y : number
|
||||||
vec)
|
z : number
|
||||||
|
-> vec
|
||||||
|
|
||||||
(vec
|
vec
|
||||||
(x number)
|
x : number
|
||||||
(y number)
|
y : number
|
||||||
(z number)
|
z : number
|
||||||
vec)
|
w : number
|
||||||
|
-> vec
|
||||||
(vec
|
|
||||||
(x number)
|
|
||||||
(y number)
|
|
||||||
(z number)
|
|
||||||
(w number)
|
|
||||||
vec)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a new `vec` from zero to four number values.
|
Creates a new `vec` from one to four number values.
|
||||||
|
|
||||||
A `vec` always has four dimensions.
|
A `vec` always has four dimensions.
|
||||||
If any of the arguments are omitted, its corresponding dimension is initialized to zero.
|
If any of the arguments are omitted, its corresponding dimension is initialized to zero.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(.x
|
vecX
|
||||||
(v vec)
|
v : vec
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.y
|
vecY
|
||||||
(v vec)
|
v : vec
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.z
|
vecZ
|
||||||
(v vec)
|
v : vec
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.w
|
vecW
|
||||||
(v vec)
|
v : vec
|
||||||
number)
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
`.x`, `.y`, `.z`, and `.w` extract the individual components of a `vec`.
|
`vecX`, `vecY`, `vecZ`, and `vecW` extract the individual components of a `vec`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -240,61 +253,54 @@ Note that mathematical operations are currently not defined for vectors.
|
||||||
You may define your own vector operations like so:
|
You may define your own vector operations like so:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def +v ; Vector addition
|
-- Vector addition
|
||||||
(fn (a b)
|
addv = \a, b ->
|
||||||
(vec
|
vec (vecX a + vecX b) (vecY a + vecY b) (vecZ a + vecZ b) (vecW a + vecW b)
|
||||||
(+ (.x a) (.x b))
|
|
||||||
(+ (.y a) (.y b))
|
|
||||||
(+ (.z a) (.z b))
|
|
||||||
(+ (.w a) (.w b)))))
|
|
||||||
|
|
||||||
; Likewise for subtraction, multiplication, and division.
|
-- Likewise for subtraction, multiplication, and division.
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that haku-defined vector operations like these are more costly the more components they operate on.
|
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.
|
Therefore, it's recommended to only define them for two dimensions, unless you really need more.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(def +v2 ; 2D vector addition
|
addv2 = \a, b ->
|
||||||
(fn (a b)
|
vec (vecX a + vecX b) (vecY a + vecY b)
|
||||||
(vec
|
|
||||||
(+ (.x a) (.x b))
|
|
||||||
(+ (.y a) (.y b)))))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Colors
|
## Colors
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(rgba
|
rgba
|
||||||
(r number)
|
r : number
|
||||||
(g number)
|
g : number
|
||||||
(b number)
|
b : number
|
||||||
(a number)
|
a : number
|
||||||
rgba)
|
-> rgba
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a new `rgba` with the given color channels.
|
Creates a new `rgba` with the given color channels.
|
||||||
Note that unlike `vec`, all color channels have to be provided to form an `rgba`.
|
Note that unlike `vec`, all color channels have to be provided to form an `rgba`.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(.r
|
rgbaR
|
||||||
(color rgba)
|
color : rgba
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.g
|
rgbaG
|
||||||
(color rgba)
|
color : rgba
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.b
|
rgbaB
|
||||||
(color rgba)
|
color : rgba
|
||||||
number)
|
-> number
|
||||||
|
|
||||||
(.a
|
rgbaA
|
||||||
(color rgba)
|
color : rgba
|
||||||
number)
|
-> number
|
||||||
```
|
```
|
||||||
|
|
||||||
`.r`, `.g`, `.b`, and `.a` extract color channels out of an `rgba`.
|
`rgbaR`, `rgbaG`, `rgbaB`, `rgbaA` extract color channels out of an `rgba`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -304,26 +310,20 @@ 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.
|
||||||
(def *rgba
|
mulRgba = \a, b ->
|
||||||
(fn (a b)
|
rgba (rgbaR a * rgbaR b) (rgbaG a * rgbaG b) (rgbaB a * rgbaB b) (rgbaA a * rgbaA b)
|
||||||
(rgba
|
|
||||||
(* (.r a) (.r b))
|
|
||||||
(* (.g a) (.g b))
|
|
||||||
(* (.b a) (.b b))
|
|
||||||
(* (.a a) (.a b)))))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If haku represented colors using an 8-bit `0` to `255` range instead, to multiply two colors together, you would have to divide them by `255` to get them back into the correct range.
|
If haku represented colors using an 8-bit `0` to `255` range instead, to multiply two colors together, you would have to divide them by `255` to get them back into the correct range.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
; NOTE: This example does NOT work correctly.
|
; NOTE: This example does NOT work correctly.
|
||||||
(def *rgba
|
mulRgba = \a, b ->
|
||||||
(fn (a b)
|
let red = (rgbaR a * rgbaR b) / 255
|
||||||
(rgba
|
let green = (rgbaG a * rgbaG b) / 255
|
||||||
(/ (* (.r a) (.r b)) 255)
|
let blue = (rgbaB a * rgbaB b) / 255
|
||||||
(/ (* (.g a) (.g b)) 255)
|
let alpha = (rgbaA a * rgbaA b) / 255
|
||||||
(/ (* (.b a) (.b b)) 255)
|
rgba red green blue alpha
|
||||||
(/ (* (.a a) (.a b)) 255))))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
||||||
|
@ -339,33 +339,18 @@ Before scribbles are drawn to the wall, colors are converted to 8-bit integers f
|
||||||
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:
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(stroke
|
stroke 128 #00000004 (vec 0 0)
|
||||||
128
|
|
||||||
(rgba 0 0 0 0.1)
|
|
||||||
(vec))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you try to to use this brush to fill up a single spot with black, you will notice that despite all the math suggesting so, the color will end up gray instead.
|
If you try to to use this brush to fill up a single spot with black, you will notice that despite all the math suggesting so, the color will end up gray instead.
|
||||||
|
|
||||||
## Data structures
|
|
||||||
|
|
||||||
```haku
|
|
||||||
(list
|
|
||||||
(... *)
|
|
||||||
list)
|
|
||||||
```
|
|
||||||
|
|
||||||
`list` is used to construct a new list.
|
|
||||||
|
|
||||||
Currently, lists do not have any operations defined on them.
|
|
||||||
However, lists made up solely of scribbles are scribbles themselves, which allows for combining scribbles together.
|
|
||||||
|
|
||||||
## Shapes
|
## Shapes
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(to-shape
|
toShape
|
||||||
(value *)
|
value : _
|
||||||
(() shape))
|
-> () | shape
|
||||||
```
|
```
|
||||||
|
|
||||||
Converts the given value to a shape.
|
Converts the given value to a shape.
|
||||||
|
@ -375,26 +360,26 @@ Converts the given value to a shape.
|
||||||
- For anything else, returns `()`.
|
- For anything else, returns `()`.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(line
|
line
|
||||||
(start vec)
|
start : vec
|
||||||
(end vec)
|
end : vec
|
||||||
shape)
|
-> shape
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a line segment shape with the provided `start` and `end` points.
|
Creates a line segment shape with the provided `start` and `end` points.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(rect
|
rect
|
||||||
(position vec)
|
position : vec
|
||||||
(size vec)
|
size : vec
|
||||||
shape)
|
-> shape
|
||||||
|
|
||||||
(rect
|
rect
|
||||||
(x number)
|
x : number
|
||||||
(y number)
|
y : number
|
||||||
(width number)
|
width : number
|
||||||
(height number)
|
height : number
|
||||||
shape)
|
-> shape
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a rectangle shape with its top-left corner at `position`, with a given `size` stretching from the top-left corner.
|
Creates a rectangle shape with its top-left corner at `position`, with a given `size` stretching from the top-left corner.
|
||||||
|
@ -402,16 +387,16 @@ Creates a rectangle shape with its top-left corner at `position`, with a given `
|
||||||
The alternative 4-argument version takes in the rectangle's X/Y coordinates, width, and height as separate arguments instead of aggregating them into a `vec`.
|
The alternative 4-argument version takes in the rectangle's X/Y coordinates, width, and height as separate arguments instead of aggregating them into a `vec`.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(circle
|
circle
|
||||||
(center vec)
|
center : vec
|
||||||
(radius number)
|
radius : number
|
||||||
shape)
|
-> shape
|
||||||
|
|
||||||
(circle
|
circle
|
||||||
(x number)
|
x : number
|
||||||
(y number)
|
y : number
|
||||||
(radius number)
|
radius : number
|
||||||
shape)
|
-> shape
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a circle shape, with its center at `center`, with the provided radius.
|
Creates a circle shape, with its center at `center`, with the provided radius.
|
||||||
|
@ -421,11 +406,11 @@ The alternative 3-argument version takes in the circle's center X/Y coordinates
|
||||||
## Scribbles
|
## Scribbles
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(stroke
|
stroke
|
||||||
(thickness number)
|
thickness : number
|
||||||
(color rgba)
|
color : rgba
|
||||||
(shape shape-like)
|
shape : shapeLike
|
||||||
scribble)
|
-> scribble
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
||||||
|
@ -433,10 +418,10 @@ Creates a stroke scribble, which outlines the provided shape with a stroke of th
|
||||||
Point shapes are drawn as squares, and `line` shapes have square caps at the line's endpoints.
|
Point shapes are drawn as squares, and `line` shapes have square caps at the line's endpoints.
|
||||||
|
|
||||||
```haku
|
```haku
|
||||||
(fill
|
fill
|
||||||
(color rgba)
|
color : rgba
|
||||||
(shape shape-like)
|
shape : shapeLike
|
||||||
scribble)
|
-> scribble
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a fill scribble, which fills in the entire area of the provided shape with a solid color.
|
Creates a fill scribble, which fills in the entire area of the provided shape with a solid color.
|
||||||
|
|
|
@ -3,7 +3,7 @@ const defaultBrush = `
|
||||||
-- Try playing around with the numbers,
|
-- Try playing around with the numbers,
|
||||||
-- and see what happens!
|
-- and see what happens!
|
||||||
|
|
||||||
stroke 8 (rgba 0 0 0 1) (vec 0 0)
|
stroke 8 #000 (vec 0 0)
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
export class BrushEditor extends HTMLElement {
|
export class BrushEditor extends HTMLElement {
|
||||||
|
|
Loading…
Reference in a new issue