Gen UI
pie.ui.* draws a UI component for a node.
This page adds one UI per section to the same graph. Any repeating wave is a sum of sine waves. The gray line is the wave we want, and the amber line is the sum so far.
Run this first. Then run the editors below in order.
pie.clear()
import numpy as np
xs = np.linspace(-6, 6, 201)
n = np.arange(1, 7)
height = pie.scalar(1.5, out='height')
freq = pie.scalar(1, out='freq')
shape = pie.scalar(0, out='shape')
mask = pie.scalar([1, 0, 0, 0, 0, 0], out='mask')
def terms(shape):
"""The six sine waves of a shape: how fast each one is, and how big."""
k = 2 * n - 1
if shape == 0: # square
return k, 4 / np.pi / k
if shape == 1: # triangle
return k, 8 / np.pi ** 2 * (-1.0) ** (n - 1) / k ** 2
return n, 2 / np.pi * (-1.0) ** (n - 1) / n # sawtooth
def goal(height, freq, shape):
t = freq * xs
waves = [np.sign(np.sin(t)), 2 / np.pi * np.arcsin(np.sin(t)), (t / np.pi + 1) % 2 - 1]
return np.column_stack([xs, height * waves[int(shape)]])
def total(height, freq, shape, mask):
k, size = terms(shape)
return np.column_stack([xs, height * np.sin(np.outer(freq * xs, k)) @ (mask * size)])
goal_path = pie.lift(goal, pie.Array[pie.Point])(height, freq, shape, hidden=True)
sum_path = pie.lift(total, pie.Array[pie.Point])(height, freq, shape, mask, hidden=True)
pie.line(goal_path[:-1], goal_path[1:], stroke='#a8a29e')
pie.line(sum_path[:-1], sum_path[1:], stroke='#b45309')
Slider
pie.ui.slider drags a Scalar between min and max. Every UI takes label, the text in front of it.
pie.ui.slider(height, min=0, max=3, step=0.1, label='height')
Drag it. Both lines grow and shrink.
Text box
pie.ui.textbox is for an exact value. It writes when you press Enter or leave the box.
pie.ui.textbox(freq, label='frequency')
Try 0.5 or 2. Text that isn't a number puts the old value back. A text box also works on a Text node.
Radio buttons
pie.ui.radio shows one button per option. The Scalar holds the chosen option's position, so shape is 0, 1 or 2 here. terms and goal read that number.
pie.ui.radio(shape, options=['square', 'triangle', 'sawtooth'], label='wave')
Checkboxes
pie.ui.checkboxes takes an Array of Scalars and shows one checkbox per item: 1 when ticked, 0 when not. That's why mask is an Array. total gets all six numbers at once, and multiplies each sine wave by its own.
pie.ui.checkboxes(mask, options=['1st', '2nd', '3rd', '4th', '5th', '6th'], label='sine waves')
Tick them in order and the amber line closes in on the gray one. A triangle gets close after two. A square always overshoots a little at its jumps, however many you tick.
LaTeX
pie.ui.latex only shows values. A Scalar, Point or Array shows its numbers, and a Text node shows as LaTeX source. Here a lifted function writes the sum as LaTeX, and reruns when any of its inputs changes.
def formula(height, freq, shape, mask):
k, size = terms(shape)
parts = [rf'{height * s:+.2f}\sin({freq * c:g}x)' for c, s, on in zip(k, size, mask) if on]
return 'y = ' + (' '.join(parts).removeprefix('+') or '0')
pie.ui.latex(pie.lift(formula, str)(height, freq, shape, mask))
Change any UI above and the formula follows.
Move a UI
Every UI can be dragged by the ⋮⋮ grip at its left. With a mouse, hover over the UI to find it.
Drop a UI on the canvas and it stays in view while you scroll. Drop it in the text and it sits between two paragraphs. Running its editor again puts it back below the editor, and reloading the page forgets where you put it.