Skip to content

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
@mlaily

This comment has been minimized.

Copy link

mlaily commented Apr 2, 2020

Copying and pasting in fable.io/repl does not work anymore.

The name spaces have changed. All the open should be replaced with:

open Elmish
open Elmish.React
open Fable.React
open Fable.React.Props
open Browser
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.