Ollin

StrokeAlignment

the same shapes stroked inside, centered on, and outside their outline, with the weight pulsing (strokeAlign, SDF)

Run
swift run --package-path Examples Example-Shapes-StrokeAlignment
Source
Examples/Shapes/StrokeAlignment/Sketch.swift
import Ollin

/// `strokeAlign(_:)` sets where a shape's stroke sits on its outline: `.inside`
/// (entirely within, so the footprint stays fixed), `.center` (the default —
/// half in, half out), or `.outside` (entirely beyond the edge, so the footprint
/// grows by the weight). The three columns — inside, center, outside, left to
/// right — draw the same shapes under each. The pale fill marks the true
/// outline; the thick stroke pulses its weight with `time`, so you can watch the
/// inside column hold its footprint while the outside column balloons. On these
/// analytic SDF shapes the inset/outset is a geometrically exact offset.
@main
final class StrokeAlignment: Sketch {
    let aligns: [StrokeAlign] = [.inside, .center, .outside]
    let names = ["inside", "center", "outside"]
    let rows = 3

    override func draw() {
        background(Color(white: 0.1))

        let pulse = 0.5 - 0.5 * cos(time * 1.6)
        strokeWeight((6 + 22 * pulse) * scale)

        let colGap = width / Double(aligns.count)
        let rowGap = height / Double(rows + 1)
        let r = min(colGap, rowGap) * 0.30

        for (col, align) in aligns.enumerated() {
            strokeAlign(align)
            let cx = colGap * (Double(col) + 0.5)
            stroke(Colormap.turbo.color(at: Double(col) / Double(aligns.count - 1)))

            // Column header (drawn unstroked so the pulsing weight doesn't reach it).
            withState {
                noStroke(); fill(.white); textAlign(.center, .middle); textSize(30 * scale)
                drawText(names[col], cx, rowGap * 0.45)
            }

            for row in 0..<rows {
                let cy = rowGap * Double(row + 1)
                fill(Color(white: 0.32))                 // marks the geometric edge
                switch row {
                case 0: drawCircle(cx, cy, r)
                case 1: drawRect(cx - r, cy - r, r * 2, r * 2, cornerRadius: r * 0.25)
                default: drawStar(cx, cy, r, r * 0.55, points: 6)
                }
            }
        }
    }
}

↑↓ move ↩ open