Last active
July 11, 2020 13:56
-
-
Save mathias-brandewinder/2254543050d98fc6063969791e006c7c to your computer and use it in GitHub Desktop.
Lorentz attractor in Fable-Elmish
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
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
Copying and pasting in fable.io/repl does not work anymore.
The name spaces have changed. All the
open
should be replaced with: