--name(), $name()
Define a function of your own, then call it anywhere in the doodle. A call is replaced by the function's body, with the arguments in place of the parameters.
Syntax
- --<name>(<param>, ...): <body>;
- --<name>(<param> = <default>, ...): <body>;
- --<name>(): { <body> }
- --<name>(<param>, ...): { result: <value>; }
- $<name>(<argument>, ...)
A definition goes at the top level of the doodle, before or after its calls, or inside an @svg(), and emits nothing.
In the body, $p is the argument passed for the parameter p.
The rules for names, arguments and errors are in the table below.
Values
The simplest body is one value. = value gives a parameter a default, used when a call leaves it out:
@grid: 6 / 60vmin _2px;
--dot(r, c = #000): radial-gradient($c $r, #0000 0);
background: $dot(30%), $dot(60%, #e6437d); Inside $() the parameter can be written without $:
@grid: 9 / 60vmin;
--wave(v): $(sin(v / 1.5) / 2 + .5);
background: #000;
border-radius: 50%;
scale: $wave(@x);
opacity: $wave(@y);
Inside $() a call is one value, so with --inc(x): x + 1, $(2 * $inc(3)) is 8.
There, in unit forms such as $px() and in the test of a @match(…) { } block, the $ can be left out:
$(2 * inc(3)) is 8 too, and @match(inc(x) > 3) { … } calls it for each cell.
Declarations and elements
A body in braces can hold several declarations, pasted where the function is called:
@grid: 7 / 60vmin _2px;
--fill(c, r): {
background: $c;
border-radius: $r;
}
@even { $fill(#000, 50%) }
@odd { $fill(#e6437d, 10%) }
The same works for elements inside @svg.
A definition can also go inside the @svg(), where only that @svg() sees it:
@grid: 1 / 60vmin;
background: @svg(
viewBox: -10 -10 20 20;
--dot(r, c = #000): circle { r: $r; fill: $c }
$dot(8) $dot(5, #e6437d) $dot(2)
); In @pattern and @scene
Calls work inside @pattern and @scene bodies too,
where a parameter reads bare. A body that assigns result gives a value,
and the names it assigns are its own for each call, so the two rings below each have their own d:
@grid: 1 / 60vmin;
--ring(p, r, w = .01): {
d: abs(length(p) - r);
result: 1 - smoothstep(0, w, d);
}
@content: @pattern(
a: $ring(pos, .3);
b: $ring(pos - vec2(.15, 0), .15, .03);
fill: mix(#fff, #000, max(a, b));
); Reference
Definitions and calls
| Part | Rule |
|---|---|
| name | letters, digits, _, and - between words, starting with a letter or _. A call reads the whole name, so $rand-int() does not call a function named rand. |
| parameter | a name such as x, size or a_b, with an optional = default, used when a call passes fewer arguments |
| body | one statement, up to its ; or through the block it opens, or any text inside braces. A call is replaced by it before the code is read, so it works wherever its text would. A statement after a body without braces that reads a parameter is reported as --f() needs {}. |
$p | the argument passed for p. Inside $(), unit forms such as $px() and the test of a @match(…) { } block a bare p works too, and reads the argument in parentheses. |
bare name(…) | in the same places, calls the function as $name(…) does, unless a built-in such as sin or pick has the name. Elsewhere, @pattern and @scene included, it is text. |
| arguments | a random or picking argument such as @r(5) is worked out once at the call, so every use of the parameter gets the same number. Other arguments are pasted as written. Outside $() an argument is text, so pass a parameter on to another function as $p. |
result | a body made only of result declarations gives the last one's value, as a CSS @function does: with --double(x): { result: $(x * 2); }, $double(2) is 4. A body with any other statement is pasted as written, so an @svg-filter element keeps its result attribute. |
| scope | calls work anywhere but inside quotes, nested @doodle code included. A definition inside an @svg() is seen only in that @svg(), where it hides one of the same name. Code inserted with @use can call the doodle's functions, and the functions it defines stay inside it. |
| no such function | $name without parentheses is still the custom property --name, and $name(…) still appends a unit, as in $px(…) |
| errors | a function that calls itself, directly or through another, is reported, and the call expands to nothing. A call with more arguments than parameters, or one that leaves a parameter with no argument and no default, is reported too. |
In @pattern and @scene
| Part | Rule |
|---|---|
| parameter | reads bare, as $p does, and p.xy takes its swizzle. It hides a built-in of the same name. |
| argument | one value, so with --f(a): a * 2, $f(x + 1) reads (x + 1) * 2 |
| expression body | one value too, so with --g(a): a + 1, $g(x) * 2 reads (x + 1) * 2 |
result | the body's statements run before the statement that calls it, and the call reads as the last result assigned. To assign it inside a match block, give it a value before the block. Such a call cannot stand in a repeat() head or in the test of a match arm. |
match blocks | @scene has no blocks, so there a match block in a result body runs as values: a name an arm assigns keeps its old value where the arm does not run |
| names | until the body assigns a name, the name reads the caller's. p: p * 2 changes the function's own copy, while $p: value writes the variable passed for p, the way to give back more than one value. |
without result | the body is pasted where it is called and writes the caller's names, as its text would |