Knotwork
the kolam line given width and a rule for which cord passes over which: the choice alternates along every cord, and that alternation is what makes it read as a weave instead of a tangle
- Run
swift run --package-path Examples Example-Patterns-Knotwork- Source
- Examples/Patterns/Knotwork/Sketch.swift
import Ollin
/// **Celtic knotwork**: the kolam line given width and a rule about who passes
/// over whom. Wherever two passes meet, one goes over, and the choice
/// alternates along every cord, so the weave holds together instead of reading
/// as a tangle.
///
/// The bands come back already broken where they dive under, so stroking them
/// in order draws the weave. No masking, and no draw order to get right.
///
/// Try it: change the field size, or move a wall, and watch a knot become a
/// different knot.
@main
final class Knotwork_Example: Sketch {
@Param(3 ... 10, icon: "arrow.left.and.right") var columns = 6.0
@Param(3 ... 10, icon: "arrow.up.and.down") var rows = 6.0
@Param(6 ... 40, icon: "scribble") var band = 22.0
@Param(icon: "square.split.2x2") var showWalls = true
let walls: [Kolam.Mirror] = [.rightOf(column: 2, row: 2), .below(column: 2, row: 2),
.rightOf(column: 2, row: 3), .below(column: 3, row: 2)]
let paper = Color(hex: 0x1D2B24)
let ink = Color(hex: 0x0B1310)
let gold = Color(hex: 0xE7C46B)
override func draw() {
background(paper)
let field = bounds.inset(by: .all(width * 0.1))
let inUse = showWalls
? walls.filter { $0.column < Int(columns) && $0.row < Int(rows) } : []
let knot = knotwork(in: field, columns: Int(columns), rows: Int(rows), mirrors: inUse)
for piece in knot.bands(gap: band * 1.35) {
let line = piece.smoothed(iterations: 3)
withState {
strokeCap(.round)
strokeJoin(.round)
stroke(ink)
strokeWeight(band)
drawPolyline(line.points, closed: line.isClosed)
stroke(gold)
strokeWeight(band - 7)
drawPolyline(line.points, closed: line.isClosed)
}
}
noStroke()
fill(gold.withAlpha(0.55))
textFont(.system); textSize(width * 0.02); textAlign(.center, .bottom)
drawText("\(knot.cords.count) cord\(knot.cords.count == 1 ? "" : "s"), \(knot.crossings.count) crossings",
width / 2, height - width * 0.035)
}
}