Fractals - Koch Snowflake ❄️

I played around a little and implemented the Koch snowflake :)

#let koch-snowflake(n) = {
  let complex-add(c1, c2) = { c1.zip(c2).map(array.sum) }
  let complex-multiply(c1, c2) = (c1.at(0) * c2.at(0) - c1.at(1) * c2.at(1), c1.at(0) * c2.at(1) + c1.at(1) * c2.at(0))
  let complex-inverse-array(a) = { a.map(c => c.map(x => -x)) }
  let complex-multiply-array(a, c) = { a.map(c1 => complex-multiply(c1, c)) }
  let complex-add-array(a1, a2) = { a1.zip(a2).map(cs => complex-add(..cs)) }
  
  let koch-snowflake-impl(n) = {
    if n == 0 {
      return (90deg, 210deg, 330deg).map(phi => (calc.cos(phi), calc.sin(phi)))
    }
    let ps1 = koch-snowflake-impl(n - 1)
    let diff = complex-add-array(ps1.slice(1) + (ps1.first(),), complex-inverse-array(ps1))
    let points = (
      ps1,
      complex-add-array(ps1, complex-multiply-array(diff, (1/3, 0))),
      complex-add-array(ps1, complex-multiply-array(diff, (0.5, -.5 * calc.sqrt(3) / 3))),
      complex-add-array(ps1, complex-multiply-array(diff, (2/3, 0))),
    )
    return array.zip(..points).join()
  }
  return koch-snowflake-impl(n)
}


#set page(width: auto, height: auto, margin: 1cm)

#path(
  fill: blue, closed: true, 
  ..koch-snowflake(6).map(a => a.map(x => x*1000pt + 1000pt))
)

Enjoy and share your own Typst fractals if you want! :typstguy_science:

12 Likes

Here is my effort from right after typst went public (changed slightly since calc.exp didn’t even exist at the time)

#let left-shift(x) = {x.slice(1) + (x.first(),)}

#let c-times(z,w) = {
  let (a,b) = z
  let (c,d) = w
  (a*c - b*d,a*d + b*c)
}

#let c-plus(z,w) = {
  let (a,b) = z
  let (c,d) = w
  (a+c,b+d)
}

#let c-exp(z) = {
  let (a,b) = z
  ( calc.exp(a) * calc.cos(b), calc.exp(a) * calc.sin(b) )
}

#let points = range(3).map(t => {c-exp( (0, 2 * calc.pi * t / 3 ) )})

#let next-points(points) = {
  for (point, point-next) in points.zip(left-shift(points)){
  ( point,
    c-plus( c-times((2/3,0),point) , c-times((1/3,0),point-next) ),
    c-times( (1/calc.sqrt(3),0) , c-plus( c-times(point,c-exp((0,calc.pi/6))) , c-times(point-next,c-exp((0,-calc.pi/6))) )),
    c-plus( c-times((1/3,0),point) , c-times((2/3,0),point-next) ),
    point-next,)
}
}

#let draw-points(points) = {
  import "canvas.typ" : canvas
  canvas(length:2cm,{
  import "draw.typ" : *
  for (point, point-next) in points.zip(left-shift(points)){
    line(point,point-next)
  }
})
}

#for n in range(6){
  draw-points(points)
  points = next-points(points)
}

7 Likes

Cool, that’s awesome!