Presumably, your program will do something. Add your working code to the project in place of Program.fs. In this case, our task is to draw spirograph curves on a Window Canvas. This is accomplished using Spirograph.fs, below.
namespace Spirograph
// open System.Windows does not automatically open all its sub-modules, so we
// have to open them explicitly as below, to get the resources noted for each.
open System // for Math.PI
open System.Windows // for Point
open System.Windows.Controls // for Canvas
open System.Windows.Shapes // for Ellipse
open System.Windows.Media // for Brushes
// ------------------------------------------------------------------------------
// This file is first in the build sequence, so types should be defined here
type DialogBoxXaml = FsXaml.XAML<"DialogBox.xaml">
type MainWindowXaml = FsXaml.XAML<"MainWindow.xaml">
type App = FsXaml.XAML<"App.xaml">
// ------------------------------------------------------------------------------
// Model: This draws the Spirograph
type MColor = | MBlue | MRed | MRandom
type Model() =
let mutable myCanvas: Canvas = null
let mutable myR = 220 // outer circle radius
let mutable myr = 65 // inner circle radius
let mutable myl = 0.8 // pen position relative to inner circle
let mutable myColor = MBlue // pen color
let rng = new Random()
let mutable myRandomColor = Color.FromRgb(rng.Next(0, 255) |> byte,
rng.Next(0, 255) |> byte,
rng.Next(0, 255) |> byte)
member this.MyCanvas
with get() = myCanvas
and set(newCanvas) = myCanvas <- newCanvas
member this.MyR
with get() = myR
and set(newR) = myR <- newR
member this.Myr
with get() = myr
and set(newr) = myr <- newr
member this.Myl
with get() = myl
and set(newl) = myl <- newl
member this.MyColor
with get() = myColor
and set(newColor) = myColor <- newColor
member this.Randomize =
// Here we randomize the parameters. You can play with the possible ranges of
// the parameters to find randomized spirographs that are pleasing to you.
this.MyR <- rng.Next(100, 500)
this.Myr <- rng.Next(this.MyR / 10, (9 * this.MyR) / 10)
this.Myl <- 0.1 + 0.8 * rng.NextDouble()
this.MyColor <- MRandom
myRandomColor <- Color.FromRgb(rng.Next(0, 255) |> byte,
rng.Next(0, 255) |> byte,
rng.Next(0, 255) |> byte)
member this.DrawSpirograph =
// Draw a spirograph. Note there is some fussing with ints and floats; this
// is required because the outer and inner circle radii are integers. This is
// necessary in order for the spirograph to return to its starting point
// after a certain number of revolutions of the outer circle.
// Start with usual recursive gcd function and determine the gcd of the inner
// and outer circle radii. Everything here should be in integers.
let rec gcd x y =
if y = 0 then x
else gcd y (x % y)
let g = gcd this.MyR this.Myr // find greatest common divisor
let maxRev = this.Myr / g // maximum revs to repeat
// Determine width and height of window, location of center point, scaling
// factor so that spirograph fits within the window, ratio of inner and outer
// radii.
// Everything from this point down should be float.
let width, height = myCanvas.ActualWidth, myCanvas.ActualHeight
let cx, cy = width / 2.0, height / 2.0 // coordinates of center point
let maxR = min cx cy // maximum radius of outer circle
let scale = maxR / float(this.MyR) // scaling factor
let rRatio = float(this.Myr) / float(this.MyR) // ratio of the radii
// Build the collection of spirograph points, scaled to the window.
let points = new PointCollection()
for degrees in [0 .. 5 .. 360 * maxRev] do
let angle = float(degrees) * Math.PI / 180.0
let x, y = cx + scale * float(this.MyR) *
((1.0-rRatio)*Math.Cos(angle) +
this.Myl*rRatio*Math.Cos((1.0-rRatio)*angle/rRatio)),
cy + scale * float(this.MyR) *
((1.0-rRatio)*Math.Sin(angle) -
this.Myl*rRatio*Math.Sin((1.0-rRatio)*angle/rRatio))
points.Add(new Point(x, y))
// Create the Polyline with the above PointCollection, erase the Canvas, and
// add the Polyline to the Canvas Children
let brush = match this.MyColor with
| MBlue -> Brushes.Blue
| MRed -> Brushes.Red
| MRandom -> new SolidColorBrush(myRandomColor)
let mySpirograph = new Polyline()
mySpirograph.Points <- points
mySpirograph.Stroke <- brush
myCanvas.Children.Clear()
this.MyCanvas.Children.Add(mySpirograph) |> ignore
Spirograph.fs is the first F# file in the compilation order, so it contains the definitions of the types we will need. Its job is to draw a spirograph on the main window Canvas based on parameters entered in a dialog box. Since there are lots of references on how to draw a spirograph, we won't go into that here.