Visualizing Prime Factors with SwiftUI
Following a recent post, I enjoyed revisiting Brent Yorgey’s Factorization Diagrams (by the way, this in-browser animation is pretty great, too.)
I immediately wanted to try some variations, and didn’t really feel like figuring out how to compile Yorgey’s Haskell, let alone how to do something interactive with it. And then I had the thought that this might be a nice way to experiment with SwiftUI: it’s graphical, declarative, and simple enough for a novice like me (or so I hoped).
After stumbling around in a Playground for while, here’s what I came up with:
VStack {
layout(n: 3) {
layout(n: 2) {
Circle().foregroundColor(.blue)
}}
}
That is, draw 3 sets of 2 blue circles each, visualizing the prime factorization: 6 = 2·3
(see the first image.)
layout
just applies some rotation, scaling, and offsets to n
copies of its argument:
// Two fairly arbitrary parameters:
let RADIUS: CGFloat = 100
func scale(_ n: Int) -> CGFloat {
return 1/CGFloat(n)
}
func layout<V:View>(n: Int, d: @escaping () -> V) -> some View {
func position(i: Int) -> some View {
let t = Angle.degrees(Double(i)*360/Double(n))
return d()
.rotationEffect(t)
.scaleEffect(scale(n))
.offset(x: RADIUS*CGFloat(sin(t.radians)),
y: RADIUS*CGFloat(-cos(t.radians)))
}
return ZStack {
ForEach(0..<n, content: position)
}
}
Additional nested calls produce more interesting designs (the second image):
layout(n: 7) {
layout(n: 5) {
layout(n: 3) {
Circle().foregroundColor(.blue)
}}}
I’m pretty happy with this as far as it goes, but so far I haven’t been able to get the fancy SwiftUI types to understand an unfolded version so I could make a single call like layoutFactors([5, 3, 2, 2])
or layoutPrimeFactors(60)
.