#plottertwitter, #generative
➡
➡
➡
Please 🐻 with me. This will be code heavy.
Full code
example is link in the slide at the end.
import List.Extra exposing (initialize, andThen)
makeGrid : Int -> Int -> List ( Float, Float )
makeGrid m n =
initialize m toFloat
|> andThen (\x -> initialize n toFloat
|> andThen (\y -> [ ( x, y ) ]))
Recipe taken from Elm-Packages : List.Extra
> initialize 2 toFloat
[0,1] : List Float
> makeGrid 2 2
[(0,0),(0,1),(1,0),(1,1)] : List ( Float, Float )
import Svg exposing (Svg)
import Svg.Attributes as Attributes
line : Float -> Float -> Float -> Float -> Svg msg
line x1 y1 x2 y2 =
Svg.line
[ Attributes.x1 <| toString x1
, Attributes.y1 <| toString y1
, Attributes.x2 <| toString x2
, Attributes.y2 <| toString y2
]
[]
type Model
= Setup ...
| Model ... (...)
type Msg
= Generate
| Draw (...)
init = update Generate (Setup ...)
update msg model =
case ( msg, model ) of
( Generate, Setup ... ) ->
-- | Tell Elm to populate random numbers into Draw
( Draw, Setup ... ) ->
-- | Create our Model for rendering
view (Model ...) =
List.mapN drawFunction initialShape randomValues
type Model
= Setup Int Int -- | Grid (m x n) in size
| Model Int Int (List Float) -- | Mix in random values
type Msg
= Generate
| Draw (List Float)
init : ( Model, Cmd Msg )
init =
update Generate (Setup 16 16)
import Random exposing (Generator)
random : Generator Float
random =
Random.float -0.5 0.5 -- | Generate a value between -0.5 and 0.5
randomList : Int -> Generator (List Float)
randomList n =
Random.list n random
update : Msg -> Model -> ( Model, Cmd Msg ) -- | Calls `view` each time
update msg model =
case ( msg, model ) of
( Generate, Setup m n ) ) ->
( model, Random.generate Draw <| randomList (m * n) )
( Draw rs, Setup m n ) ->
( Model m n rs, Cmd.none )
_ ->
( model, Cmd.none )
import Svg.Attributes exposing (transform)
view : Model -> Svg Msg
view (Model m n rs) =
let
flip r = if r > 0 then 0 else 90
in
List.map3
(\( dx, dy ) (( x1, y1 ), ( x2, y2 )) r ->
Svg.g
[ transform <|
(" translate(" ++ toString dx ++ "," ++ toString dy ++ ") ")
++ " rotate(" ++ toString (flip r) ++ ") "
]
[ line x1 y1 x2 y2 ]
)
(makeGrid m n)
(List.repeat (m * n) ( ( -0.5, -0.5 ), ( 0.5, 0.5 ) ))
rs
Creating the illusion of order out of pure randomness.
type Model =
Setup Int | Model Int Curtain
type Curtain =
Curtain (List (List Float)) (List (List Float))
view : Model -> Svg Msg
view (Model n (Curtain a b)) =
Svg.g
[]
-- | creates `List (List (Float, Float))` with 100 segments each.
(initialiseLines 100
-- | Add random and shepherded random values to each list of points
|> List.map2 (map2First (+)) a
|> List.map2 (map2First (+)) (accumulateList a)
|> List.map2 (map2First (+)) b
|> List.map2 (map2Second (+)) (accumulateList b)
-- | Draw each line
|> List.map lines
)
Trivia: it was originally created to simulate plants in nature.
Exmaple taken from The Algorithm Beauty of Plants
Starting point: (0, 0), angle: 0° (->)
Starting state: DRDRDRD
Starting state: DRDRDRD
Rule: D -> DDRDLDRDRDD
type alias Rule a =
a -> List a
apply : Rule a -> List a -> List a
apply rule states =
List.concatMap (\s -> rule s) states
type State
= D
| S
| L
| R
turtle : List State -> ( Float, Float ) -> Float -> List Segment
turtle states point angle =
let
move ( x, y ) a =
( x + cos (degrees a), y + sin (degrees a) )
next ( p, a ) state =
case state of
D -> ( ( move p a, a ), [ PathD.L (move p a) ] )
S -> ( ( move p a, a ), [ PathD.M (move p a) ] )
L -> ( ( p, a - 90 ), [] )
R -> ( ( p, a + 90 ), [] )
in
List.concat (Tuple.second <| mapAccuml next ( point, angle ) states)
Because Elm doesn't (yet) have:
We use port — Elm's foreign function interface to Javascript.
port print : String -> Cmd msg
app.ports.print.subscribe( () => {
const node = document.getElementById('frame');
const payload = { svg: node.outerHTML };
http('POST', api(port, '/print'), payload)
.then(console.log)
.catch(console.error);
});