Hokusai

Using HTML5 canvas

Hokusai and Julia set fractals


×
1: 
2: 
3: 
4: 
5: 
6: 
[<ReflectedDefinition>]
module Program

open System
open FunScript
open FunScript.TypeScript

Complex numbers

Simple implementation of complex numbers for JavaScript.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
type Complex = 
  | Complex of float * float
  static member Abs(Complex(r, i)) =
    let num = Math.Abs(r)
    let num2 = Math.Abs(i)
    if (num > num2) then
      let num3 = num2 / num
      num * Math.Sqrt(1.0 + num3 * num3)
    elif num2 = 0.0 then
      num
    else
      let num4 = num / num2
      num2 * Math.Sqrt(1.0 + num4 * num4)    
  static member (+) (Complex(r1, i1), Complex(r2, i2)) = 
    Complex(r1+r2, i1+i2)

module ComplexModule =
  let Pow(Complex(r, i), power) =
    let num = Complex.Abs(Complex(r, i))
    let num2 = Math.Atan2(i, r)
    let num3 = power * num2
    let num4 = Math.Pow(num, power)
    Complex(num4 * Math.Cos(num3), num4 * Math.Sin(num3))

Calculating the Julia set

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
/// Constant that generates nice fractal
let c = Complex(-0.70176, -0.3842)

/// Generates sequence for given coordinates
let iterate x y =
  let rec loop current = seq { 
    yield current
    yield! loop (ComplexModule.Pow(current, 2.0) + c) }
  loop (Complex(x, y))

let countIterations max x y = 
  iterate x y
  |> Seq.take (max - 1)
  |> Seq.takeWhile (fun v -> Complex.Abs(v) < 2.0)
  |> Seq.length

Generating the color palette

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
// Transition between colors in 'count' steps
let (--) clr count = clr, count
let (-->) ((r1,g1,b1), count) (r2,g2,b2) = [
  for c in 0 .. count - 1 ->
    let k = float c / float count
    let mid v1 v2 = 
      (float v1 + ((float v2) - (float v1)) * k) 
    (mid r1 r2, mid g1 g2, mid b1 b2) ]

// Palette with colors used by Hokusai
let palette = 
  [| // 3x sky color & transition to light blue
     yield! (245,219,184) --3--> (245,219,184) 
     yield! (245,219,184) --4--> (138,173,179)
     // to dark blue and then medium dark blue
     yield! (138,173,179) --4--> (2,12,74)
     yield! (2,12,74)     --4--> (61,102,130)
     // to wave color, then light blue & back to wave
     yield! (61,102,130)  -- 8--> (249,243,221) 
     yield! (249,243,221) --32--> (138,173,179) 
     yield! (138,173,179) --32--> (61,102,130) |]

Drawing the fractal

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
// Specifies what range of the set to draw
let w = -0.4, 0.4
let h = -0.95, -0.35

// Create bitmap that matches the size of the canvas
let width = 400.0
let height = 300.0

/// Set pixel value in ImageData to a given color
let setPixel (img:ImageData) x y width (r, g, b) =
  let index = (x + y * int width) * 4
  img.data.[index+0] <- r
  img.data.[index+1] <- g
  img.data.[index+2] <- b
  img.data.[index+3] <- 255.0

/// Dynamic operator that returns HTML element by ID
let (?) (doc:Document) name :'R = 
  doc.getElementById(name) :?> 'R

/// Render fractal asynchronously with sleep after every line
let render () = async {
  // Get <canvas> element & create image for drawing
  let canv : HTMLCanvasElement = Globals.document?canvas
  let ctx = canv.getContext_2d()
  let img = ctx.createImageData(float width, float height)
    
  // For each pixel, transform to the specified range
  // and get color using countInterations and palette
  for x in 0 .. int width - 1 do
    for y in 0 .. int height - 1 do 
      let x' = (float x / width * (snd w - fst w)) + fst w
      let y' = (float y / height * (snd h - fst h)) + fst h
      let it = countIterations palette.Length x' y' 
      setPixel img x y width palette.[it]

    // Insert non-blocking waiting & update the fractal
    do! Async.Sleep(1)
    ctx.putImageData(img, 0.0, 0.0) }

/// Setup button event handler to start the rendering
let main() =
  let go : HTMLButtonElement = Globals.document?go
  go.addEventListener_click(fun _ -> 
    render() |> Async.StartImmediate; null)  

Start local FunScript server

1: 
do Runtime.Run(directory="Web", port=(new System.Random()).Next(10000) + 8000)
Multiple items
type ReflectedDefinitionAttribute =
  inherit Attribute
  new : unit -> ReflectedDefinitionAttribute

Full name: Microsoft.FSharp.Core.ReflectedDefinitionAttribute

--------------------
new : unit -> ReflectedDefinitionAttribute
module Program
namespace System
namespace FunScript
namespace FunScript.TypeScript
Multiple items
union case Complex.Complex: float * float -> Complex

--------------------
type Complex =
  | Complex of float * float
  static member Abs : Complex -> float
  static member ( + ) : Complex * Complex -> Complex

Full name: Program.Complex
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
static member Complex.Abs : Complex -> float

Full name: Program.Complex.Abs
val r : float
val i : float
val num : float
type Math

Full name: FunScript.TypeScript.Math
Math.Abs(value: decimal) : decimal
Math.Abs(value: float) : float
Math.Abs(value: float32) : float32
Math.Abs(value: int64) : int64
Math.Abs(value: int) : int
Math.Abs(value: int16) : int16
Math.Abs(value: sbyte) : sbyte
val num2 : float
val num3 : float
Math.Sqrt(d: float) : float
val num4 : float
val r1 : float
val i1 : float
val r2 : float
val i2 : float
val Pow : Complex * power:float -> Complex

Full name: Program.ComplexModule.Pow
val power : float
static member Complex.Abs : Complex -> float
Math.Atan2(y: float, x: float) : float
Math.Pow(x: float, y: float) : float
Math.Cos(d: float) : float
Math.Sin(a: float) : float
val c : Complex

Full name: Program.c


 Constant that generates nice fractal
val iterate : x:float -> y:float -> seq<Complex>

Full name: Program.iterate


 Generates sequence for given coordinates
val x : float
val y : float
val loop : (Complex -> seq<Complex>)
val current : Complex
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
module ComplexModule

from Program
val countIterations : max:int -> x:float -> y:float -> int

Full name: Program.countIterations
val max : int
module Seq

from Microsoft.FSharp.Collections
val take : count:int -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.take
val takeWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.takeWhile
val v : Complex
val length : source:seq<'T> -> int

Full name: Microsoft.FSharp.Collections.Seq.length
val clr : 'a
val count : 'b
val r1 : int
val g1 : int
val b1 : int
val count : int
val r2 : int
val g2 : int
val b2 : int
val c : int
val k : float
val mid : (int -> int -> float)
val v1 : int
val v2 : int
val palette : (float * float * float) []

Full name: Program.palette
val w : float * float

Full name: Program.w
val h : float * float

Full name: Program.h
val width : float

Full name: Program.width
val height : float

Full name: Program.height
val setPixel : img:ImageData -> x:int -> y:int -> width:float -> r:float * g:float * b:float -> unit

Full name: Program.setPixel


 Set pixel value in ImageData to a given color
val img : ImageData
type ImageData

Full name: FunScript.TypeScript.ImageData
val x : int
val y : int
val width : float
val g : float
val b : float
val index : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
property ImageData.data: float array
val doc : Document
type Document =
  interface
    inherit MSNodeExtensions
    inherit MSResourceMetadata
    inherit DocumentEvent
    inherit MSEventAttachmentTarget
    inherit NodeSelector
    inherit Node
  end

Full name: FunScript.TypeScript.Document
val name : string
member Document.getElementById : elementId:string -> HTMLElement
val render : unit -> Async<unit>

Full name: Program.render


 Render fractal asynchronously with sleep after every line
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val canv : HTMLCanvasElement
type HTMLCanvasElement =
  interface
    inherit HTMLElement
  end

Full name: FunScript.TypeScript.HTMLCanvasElement
type Globals

Full name: FunScript.TypeScript.Globals
property Globals.document: Document
val ctx : CanvasRenderingContext2D
member HTMLCanvasElement.getContext_2d : unit -> CanvasRenderingContext2D
member CanvasRenderingContext2D.createImageData : imageDataOrSw:obj * ?sh:float -> ImageData
val x' : float
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val y' : float
val it : int
property Array.Length: int
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
member CanvasRenderingContext2D.putImageData : imagedata:ImageData * dx:float * dy:float * ?dirtyX:float * ?dirtyY:float * ?dirtyWidth:float * ?dirtyHeight:float -> unit
val main : unit -> unit

Full name: Program.main


 Setup button event handler to start the rendering
val go : HTMLButtonElement
type HTMLButtonElement =
  interface
    inherit MSDataBindingExtensions
    inherit HTMLElement
  end

Full name: FunScript.TypeScript.HTMLButtonElement
member HTMLElement.addEventListener_click : listener:Func<MouseEvent,obj> * ?useCapture:bool -> unit
static member Async.StartImmediate : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
namespace System.Runtime
Multiple items
type Random =
  new : unit -> Random + 1 overload
  member Next : unit -> int + 2 overloads
  member NextBytes : buffer:byte[] -> unit
  member NextDouble : unit -> float

Full name: System.Random

--------------------
Random() : unit
Random(Seed: int) : unit

Live demo

Click here to open the sample live, running using JavaScript in a dialog window.

To look at the generated JavaScript, view the source of this web page and scroll to the end of the file.

Tutorials

Learn FunScript from the following introductory tutorials

Samples

Browse other amazing samples created with FunScript

Fork me on GitHub