Parameters
the typed @Param family, shown in grouped inspector cards under OllinLive
- Run
swift run --package-path Examples Example-Live-Parameters- Source
- Examples/Live/Parameters/Sketch.swift
import Foundation
import Ollin
// Parameters: tunable parameters with @Param. Run it under the live host to get
// live controls in the inspector:
//
// swift run OllinLive Examples/Live/Parameters/Sketch.swift
//
// Standalone (`swift run Example-Live-Parameters`) shows the same parameters in the
// inspector panel: View ▸ Show Inspector (⌘/).
//
// The control follows the property's type: Double → slider, Int → stepper,
// Bool → toggle, a ParamOption enum → menu, Color → color well, Palette → a
// strip of swatches, Ramp → a band with a handle per stop, Easing → a menu of
// the named curves, ResponseCurve → a graph whose points drag, and a list of
// numbers → a row of bars. `group:` names an inspector section, `icon:` gives the row
// an SF Symbol, and `group: .folded("Advanced")` makes a section that starts
// closed behind a disclosure row (open it and it stays open for this sketch).
// Numeric value boxes scrub: drag across one to change it (Option = fine,
// Shift = coarse), or click to type.
//
// Under the live host, the button below the parameters writes the ones you changed
// into the @Param lines here, so a set you like survives the run. Copy the
// file somewhere of your own first if you would rather not edit this one.
//
// A parameter can also be set from the command line, on any export path and on the
// window, so one render can differ without editing the file:
//
// swift run Example-Live-Parameters --export keeper.png --param radius=40 --param paper=#101018
enum RingStyle: String, CaseIterable, ParamOption { case rings, dots, beads }
@main
final class Parameters: Sketch {
@Param(10...375, icon: "circle.dashed", group: "Rings") var radius = 175.0
@Param(1...12, icon: "circle.grid.2x2", group: "Rings") var rings = 5
@Param(style: .segmented, icon: "sparkles", group: "Rings") var style: RingStyle = .rings
@Param(x: 0...1080, y: 0...1080, style: .pad,
icon: "arrow.up.and.down.and.arrow.left.and.right",
group: "Rings") var anchor = Vector2(540, 540)
@Param(0...4, icon: "speedometer", group: "Motion") var speed = 1.0
@Param(icon: "wind", group: "Motion") var breathe = true
// A curve is a value like any other: this one spaces the rings, tight at
// the ends and open in the middle, or the other way about.
@Param("Spacing", icon: "chart.line.uptrend.xyaxis", group: "Motion")
var spacing: Easing = .linear
@Param(icon: "paintpalette", group: "Look") var paper: Color = .white
// A color each for the rings, and a gradient the dots read along.
@Param(count: 2...8, icon: "swatchpalette", group: "Look")
var inks = Palette(Color(hex: 0x1B1B1B), Color(hex: 0xE4572E), Color(hex: 0x2E86AB))
@Param(count: 2...6, icon: "circle.lefthalf.filled", group: "Look")
var fade = Ramp([Color(hex: 0xE4572E), Color(hex: 0x2E86AB)])
@Param(0.5...12, step: 0.5, icon: "lineweight", group: "Look") var weight = 2.5
// A row of bars is a list of numbers: here a weight for each ring in
// turn, repeating, so the rings read heavy, light, heavier, light.
@Param(0...1, icon: "chart.bar", group: "Look") var accents = [1, 0.3, 0.7, 0.3]
// A curve shaped by dragging its points, read as `taper(t)` the way an
// easing reads: how big each dot or bead is along its ring's turn.
@Param(icon: "chart.xyaxis.line", group: "Look")
var taper = ResponseCurve([(0, 1), (0.5, 0.35), (1, 1)])
@Param(icon: "character.cursor.ibeam", group: "Look") var caption = "rings"
// Parameters worth having but not worth a first glance: a `.folded` group
// starts closed behind its header, and opening it is remembered for this
// sketch, so it stays how you left it.
@Param(0...30, icon: "waveform.path", group: .folded("Advanced")) var jitter = 0.0
@Param(-2...2, step: 0.25, icon: "arrow.clockwise", group: .folded("Advanced")) var orbit = 0.0
override func draw() {
background(paper)
for i in 0..<rings {
strokeWeight(weight * (0.25 + accents[i % accents.count]) * scale)
let t = Double(i) / Double(max(rings - 1, 1))
let phase: Double = time * speed + t * .tau
let swell: Double = breathe ? sin(phase) * 30 : 0
let wobble: Double = jitter > 0 ? (noise(t * 4, time * 0.4) * 2 - 1) * jitter : 0
let r: Double = (radius * (0.25 + spacing(t)) + swell + wobble) * scale
switch style {
case .rings:
noFill()
stroke(inks[i])
drawCircle(anchor.x * scale, anchor.y * scale, r)
case .dots, .beads:
let count = style == .dots ? 48 : 12
let dot = (style == .dots ? 2.0 : 6.0) * weight * scale
fill(fade.color(at: t))
noStroke()
for j in 0..<count {
let turn = Double(j) / Double(count)
let a = turn * .tau + time * (speed * 0.2 + orbit)
drawCircle(anchor.x * scale + cos(a) * r, anchor.y * scale + sin(a) * r,
dot * (0.2 + taper(turn)))
}
}
}
if !caption.isEmpty { drawCaption(caption) }
}
}