PixelField
a blank image painted pixel by pixel into a flowing colormap field, then drawn under a tint that animates between warm and cool, with a row of swatches read back with get (Image(width:height:), image[x, y], tint)
- Run
swift run --package-path Examples Example-Images-PixelField- Source
- Examples/Images/PixelField/Sketch.swift
import Ollin
/// Authoring an image pixel by pixel, then recoloring it at draw time. `setup()`
/// makes a blank `Image` and writes every pixel with `image[x, y] = …`, painting
/// a flowing colormap field with no asset on disk. `draw()` then draws that field
/// large under an animated `tint(_:)` — a warm↔cool wash that also pulses its
/// opacity, letting the background show through at the thin end.
///
/// The strip of swatches along the bottom reads the field back with `image[x, y]`
/// (a get): they show the *authored* colors, unaffected by the tint, because
/// `tint` only multiplies as the image is drawn — it never edits the stored pixels.
@main
final class PixelField: Sketch {
var field: Image?
override func setup() {
noStroke()
// A blank square image, authored one pixel at a time.
let size = 120
let image = Image(width: size, height: size)
for py in 0..<size {
for px in 0..<size {
let u = Double(px) / Double(size - 1)
let v = Double(py) / Double(size - 1)
// A smooth diagonal wave blended with value noise, mapped through
// a perceptual colormap.
let wave = unipolar(sin((u + v) * .tau))
let value = wave * 0.6 + noise(u * 3, v * 3) * 0.4
image[px, py] = Colormap.turbo.color(at: min(1, max(0, value)))
}
}
field = image
}
override func draw() {
background(Color(white: 0.08))
guard let field else { return }
let side = width * 0.62
let x = (width - side) / 2
let y = height * 0.12
// Animated tint: a warm↔cool wash whose alpha pulses, so the image fades
// toward the background and back.
let k = unipolar(sin(time * 2.4))
withState {
tint(Color(red: 0.7 + 0.3 * k, green: 0.85, blue: 1.0 - 0.3 * k,
alpha: 0.55 + 0.45 * k))
drawImage(field, x, y, side, side)
}
// Read the field's middle row back (a get) and lay the true colors out as
// swatches — untouched by the tint above.
let count = 16
let gap = side / Double(count)
let sy = y + side + height * 0.05
for i in 0..<count {
let t = Double(i) / Double(count - 1)
let px = Int(t * Double(field.width - 1))
fill(field[px, field.height / 2])
drawRect(x + Double(i) * gap, sy, gap - 6 * scale, gap - 6 * scale)
}
}
}