Instantly share code, notes, and snippets.

Embed
What would you like to do?
Lorentz attractor in Fable-Elmish
(*
Simulation of the Lorentz attractor, using Fable.
If you want to see this code in action, just copy this code into the Fable REPL:
https://fable.io/repl/
*)
module App
open Elmish
open Elmish.React
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Import.Browser
// MODEL
// Lorentz attractor
let lorentz =
let sigma = 10.
let beta = 8. / 3.
let rho = 28.
fun (x, y, z) ->
let dx = sigma * (y-x)
let dy = rho * x - y - x * z
let dz = x * y - beta * z
dx, dy, dz
let nextLorentz step (x, y, z) =
let dx, dy, dz = lorentz (x, y, z)
x + dx * step,
y + dy * step,
z + dz * step
// isometric projection: plotting 3D onto 2D surface
open System
let PI = Math.PI
let xAngle = 3. * PI / 4.
let yAngle = PI / 4.
let zAngle = PI / 2.
let project (x, y, z) =
let X =
x * cos xAngle +
y * cos yAngle +
z * cos zAngle
let Y =
x * sin xAngle +
y * sin yAngle +
z * sin zAngle
X, Y
let minx = -15.
let maxx = 15.
let miny = -10.0
let maxy = 70.
let spanx = maxx - minx
let spany = maxy - miny
let scale (width, height) (x, y) =
width * (x - minx) / spanx,
height * (y - miny) / spany
type Dot3D = float * float * float
type Model = {
Trace: int
Dots: Dot3D []
Width: float
Height: float
}
type Msg =
| NextDot
let init () : Model =
{
Trace = 300
Dots = [| 10., 10., 10. |]
Width = 440.
Height = 380.
}
// UPDATE
let update (msg:Msg) (model:Model) =
match msg with
| NextDot ->
let previous = model.Dots.[0]
let next = nextLorentz 0.01 previous
{ model with
Dots =
Array.append
[| next |]
(model.Dots |> Array.truncate model.Trace)
}
// VIEW (rendered with React)
let view (model:Model) dispatch =
div
[]
[
svg
[ SVGAttr.Width(model.Width); SVGAttr.Height(model.Height) ]
[
yield
rect [ SVGAttr.Width(model.Width); SVGAttr.Height(model.Height); SVGAttr.Fill("black")] []
for dot in model.Dots ->
dot
|> project
|> scale (model.Width, model.Height)
|> fun (x, y) ->
circle [
SVGAttr.Cx(x)
SVGAttr.Cy(y);
SVGAttr.R(1);
SVGAttr.Fill("white")
] []
]
]
// Subscription
let timer initial =
let sub dispatch =
window.setInterval(
(fun _ -> dispatch NextDot),
10
)
|> ignore
Cmd.ofSub sub
// App
Program.mkSimple init update view
|> Program.withReact "elmish-app"
|> Program.withSubscription timer
|> Program.withConsoleTrace
|> Program.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment