Welcome to day 2 of the F# Advent Calendar in English, and don’t miss Scott Wlaschin’s introduction to property-based testing from yesterday.
In A Christmas Carol Charles Dickens wrote of cold winters with snow as a matter of course. White Christmases were common during the Little Ice Age that lasted from the 1550s to the 1850s. Nowadays the chances of a snowfall on Christmas day are much lower, but the imagery of a white Christmas persists.
In this post we’ll generate our snowflakes instead.
Koch Snowflake
The Koch snowflake is a mathematical curve constructed from an equilateral triangle where each line segment is recursively altered:
The picture above was generated in the F# REPL, using WinForms to display a bitmap:
let snowflake (graphics:Graphics) length =
use pen = new Pen(Color.White)
let angle = ref 0.0
let x = ref ((float width/2.0) - length/2.0)
let y = ref ((float height/2.0) - length/3.0)
let rec segment n depth =
if depth = 0 then
line n
else
segment (n/3.0) (depth-1)
rotate -60.0
segment (n/3.0) (depth-1)
rotate 120.0
segment (n/3.0) (depth-1)
rotate -60.0
segment (n/3.0) (depth-1)
and line n =
let r = !angle * Math.PI / 180.0
let x2 = !x + cos(r) * n
let y2 = !y + sin(r) * n
graphics.DrawLine(pen, float32 !x,float32 !y, float32 x2, float32 y2)
x := x2
y := y2
and rotate a =
angle := !angle + a
let depth = 5
segment length depth
rotate 120.0
segment length depth
rotate 120.0
segment length depth
The full snippet is available on F# Snippets: http://fssnip.net/oA
Paper and Scissors
Snowflakes can be created by folding paper and cutting holes with scissors. We can get a similar effect using transparent polygons and rotational symmetry:
Here the polygons are selected randomly and like snowflakes each one is different:
let paperSnowflake () =
let image = new Bitmap(int width, int height)
use graphics = Graphics.FromImage(image)
use brush = new SolidBrush(Color.Black)
graphics.FillRectangle(brush, 0, 0, int width, int height)
graphics.TranslateTransform(float32 (width/2.0),float32 (height/2.0))
let color = Color.FromArgb(128,0,128,255)
use brush = new SolidBrush(color)
let rand = Random()
let polys =
[for i in 1..12 ->
let w = rand.Next(20)+1 // width
let h = rand.Next(20)+1 // height
let m = rand.Next(h) // midpoint
let s = rand.Next(30) // start
[|0,s; -w,s+m; 0,s+h; w,s+m|]
]
for i in 0.0..60.0..300.0 do
graphics.RotateTransform(float32 60.0)
let poly points =
let points = [|for (x,y) in points -> Point(x*5,y*5)|]
graphics.FillPolygon(brush,points)
polys |> List.iter poly
image
The full snippet is on F# Snippets: http://fssnip.net/oB
Another interesting method of generating snowflakes is cellular automata, but I’ll leave that as an exercise for the reader.
Happy holidays!