Preparing for the removal of positional functions from the language. The first big step is to change all our KCL code examples, test code, public samples etc to all use keyword functions. Apologies for how large this PR is. Most of it is: - Changing example KCL that defined its own functions, so the functions now use keyword arguments rather than positional arguments. E.g. change `cube([20, 20])` to be `cube(center = [20, 20])`. - Some parts of the code assumed positional code and didn't handle keyword calls, e.g. the linter would only check for positional calls to startSketchOn. Now they should work with either positional or keyword. - Update all the artifacts This does _not_ remove support for positional calls. That will be in a follow-up PR.
123 lines
3.0 KiB
Plaintext
123 lines
3.0 KiB
Plaintext
// L-System KCL
|
|
// Zoo Corporation ⓒ 2024
|
|
|
|
// Comparators
|
|
|
|
fn cond(@bools) {
|
|
return fn(a, b) {
|
|
x = min([max([-1, a-b]), 1]) + 1
|
|
return bools[x]
|
|
}
|
|
}
|
|
|
|
fn Not(@b) { return if b { false } else { true } }
|
|
fn And(a, b) { return if a { if b { true } else { false } } else { false }}
|
|
fn Or(a, b) { return if a { true } else { if b { true } else { false }}}
|
|
|
|
Eq = cond([false, true, false])
|
|
Lt = cond([true, false, false])
|
|
Gt = cond([false, false, true])
|
|
|
|
fn Lte(a, b) { return Not(Gt(a = a, b = b)) }
|
|
fn Gte(a, b) { return Not(Lt(a = a, b = b)) }
|
|
|
|
// L-system
|
|
// Note: it was most concise to encode productions directly in axioms.
|
|
// Change them as you need.
|
|
|
|
fn setSketch(@state, q) {
|
|
return {
|
|
depthMax = state.depthMax,
|
|
depth = state.depth + 1,
|
|
currentLength = state.currentLength,
|
|
factor = state.factor,
|
|
currentAngle = state.currentAngle,
|
|
angle = state.angle,
|
|
q
|
|
}
|
|
}
|
|
|
|
fn setDepth(@state, q) {
|
|
return {
|
|
depthMax = state.depthMax,
|
|
depth = q,
|
|
currentLength = state.currentLength,
|
|
factor = state.factor,
|
|
currentAngle = state.currentAngle,
|
|
angle = state.angle,
|
|
q = state.q
|
|
}
|
|
}
|
|
|
|
fn setAngle(@state, q) {
|
|
return {
|
|
depthMax = state.depthMax,
|
|
depth = state.depth,
|
|
currentLength = state.currentLength,
|
|
factor = state.factor,
|
|
currentAngle = q,
|
|
angle = state.angle,
|
|
q = state.q
|
|
}
|
|
}
|
|
|
|
fn setLength(@state, q) {
|
|
return {
|
|
depthMax = state.depthMax,
|
|
depth = state.depth,
|
|
currentLength = q,
|
|
factor = state.factor,
|
|
currentAngle = state.currentAngle,
|
|
angle = state.angle,
|
|
q = state.q
|
|
}
|
|
}
|
|
|
|
fn Gt2(@state) { return setLength(state, q = state.currentLength * state.factor) }
|
|
fn Lt2(@state) { return setLength(state, q = state.currentLength / state.factor) }
|
|
fn Add(@state) { return setAngle(state, q = rem(state.currentAngle - state.angle, divisor = 360)) }
|
|
fn Sub(@state) { return setAngle(state, q = rem(state.currentAngle + state.angle, divisor = 360)) }
|
|
|
|
// Only necessary to get around recursion limitations...
|
|
fn F(@state, F) {
|
|
return if Lt(a = state.depth, b = state.depthMax) {
|
|
stateNext = state |> setDepth(%, q = state.depth + 1)
|
|
|
|
// Produce
|
|
// Note:if you need [ and ], just save state to a variable.
|
|
stateNext
|
|
|> F(%, F = F) |> Sub(%) |> F(%, F = F)
|
|
|> Add(%) |> Add(%)
|
|
|> F(%, F = F) |> Sub(%) |> F(%, F = F)
|
|
|> setDepth(%, q = stateNext.depth - 1)
|
|
|
|
} else {
|
|
// Pass onto the next instruction
|
|
state |> setSketch(%, q = angledLine(state.q, angle = state.currentAngle, length = state.currentLength))
|
|
}
|
|
}
|
|
|
|
fn LSystem(@args, axioms) {
|
|
myThing = startSketchOn(XY)
|
|
|> startProfile(at = [0, 0])
|
|
return axioms({
|
|
depthMax = args.iterations,
|
|
depth = 0,
|
|
currentLength = 1.0,
|
|
factor = args.factor,
|
|
currentAngle = 0,
|
|
angle = args.angle,
|
|
q = myThing,
|
|
})
|
|
}
|
|
|
|
LSystem({
|
|
iterations = 1,
|
|
factor = 1.36,
|
|
angle = 60,
|
|
}, axioms = fn(@q) {
|
|
result = q |> F(%, F = F) |> Add(%) |> Add(%) |> F(%, F = F) |> Add(%) |> Add(%) |> F(%, F = F)
|
|
return result.q
|
|
})
|
|
|