Initial commite; import clouds graphic

master
Noah Gordon 2019-05-21 14:51:31 -04:00
commit 4856471a32
31 changed files with 558 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
elm.js

1
build-live.sh Executable file
View File

@ -0,0 +1 @@
elm-live src/Main.elm -- --output=elm.js $@

1
build.sh Executable file
View File

@ -0,0 +1 @@
elm make src/Main.elm --output=elm.js $@

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
elm-stuff/0.19.0/Main.elmi Normal file

Binary file not shown.

BIN
elm-stuff/0.19.0/Main.elmo Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
elm-stuff/0.19.0/Model.elmi Normal file

Binary file not shown.

BIN
elm-stuff/0.19.0/Model.elmo Normal file

Binary file not shown.

BIN
elm-stuff/0.19.0/View.elmi Normal file

Binary file not shown.

BIN
elm-stuff/0.19.0/View.elmo Normal file

Binary file not shown.

Binary file not shown.

35
elm.json Normal file
View File

@ -0,0 +1,35 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"avh4/elm-color": "1.0.0",
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/json": "1.1.3",
"elm/random": "1.0.0",
"elm/svg": "1.0.1",
"elm/time": "1.0.0",
"elm-community/list-extra": "8.1.0",
"elm-community/random-extra": "3.0.0",
"elm-community/typed-svg": "5.0.1",
"ianmackenzie/elm-geometry": "1.2.1",
"mdgriffith/elm-ui": "1.1.0"
},
"indirect": {
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2",
"ianmackenzie/elm-float-extra": "1.0.1",
"ianmackenzie/elm-interval": "1.0.1",
"ianmackenzie/elm-triangular-mesh": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

52
index.html Normal file
View File

@ -0,0 +1,52 @@
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="elm.js"></script>
<style>
.cloud-row {
transform-origin: center;
}
</style>
</head>
<body style="margin:0; padding:0">
<script type="text/javascript">
var app = Elm.Main.init({
flags: {
window: {
width: window.innerWidth,
height: window.innerHeight
},
time: (new Date()).getTime()
}
});
if (navigator.requestMIDIAccess) {
console.log('This browser supports WebMIDI!');
} else {
console.log('WebMIDI is not supported in this browser.');
}
navigator.requestMIDIAccess()
.then(onMIDISuccess, onMIDIFailure);
function onMIDIFailure() {
console.log('Could not access your MIDI devices.');
}
function onMIDISuccess(midiAccess) {
for (var input of midiAccess.inputs.values()) {
input.onmidimessage = getMIDIMessage;
}
}
function getMIDIMessage(midiMessage) {
app.ports.midiInput.send({
status: midiMessage.data[0],
dataOne: midiMessage.data[1],
dataTwo: midiMessage.data[2]
});
}
</script>
</body>
</html>

139
src/Clouds/EffectView.elm Normal file
View File

@ -0,0 +1,139 @@
module Clouds.EffectView exposing (draw)
import Arc2d
import Clouds.Model exposing (..)
import Color exposing (Color, rgb)
import Html exposing (Html)
import Html.Attributes exposing (id)
import Messages exposing (Message)
import Point2d as Point
import Svg.Attributes
import TypedSvg exposing (..)
import TypedSvg.Attributes as Attributes exposing (..)
import TypedSvg.Core exposing (..)
import TypedSvg.Types exposing (..)
draw : Model -> Html Message
draw model =
svg
[ width (model.window.width - 200 |> px)
, height (model.window.height |> px)
]
[ defs []
[ linearGradient
[ id "gradient"
, gradientTransform [ Rotate 90 0 0 ]
]
[ stop [ offset "0%", stopColor "#7593ab" ] []
, stop [ offset "15%", stopColor "#cbc3c1" ] []
, stop [ offset "25%", stopColor "#cbc3c1" ] []
, stop [ offset "30%", stopColor "#cab0ac" ] []
, stop [ offset "35%", stopColor "#8ea5ba" ] []
, stop [ offset "65%", stopColor "#afbdc6" ] []
, stop [ offset "100%", stopColor "#001a66" ] []
]
]
, rect
[ width (100 |> percent)
, height (100 |> percent)
, Svg.Attributes.fill "url(#gradient)"
]
[]
, svg
[ x (px 0)
, y (px 260)
]
(List.map drawCloudRow model.cloudRows)
]
drawCloudRow : CloudRow -> Svg Message
drawCloudRow row =
svg
[ x (px 0)
, y (px (row.y - 10))
, height (px row.height)
, transform
[ Scale row.scale 1 ]
, class [ "cloud-row" ]
, opacity (Opacity row.opacity)
]
(List.map (drawCloud row.height) row.clouds)
drawCloud : Float -> Cloud -> Svg Message
drawCloud rowHeight cloud =
svg
[ height (px rowHeight)
, width (px cloud.width)
, x (px cloud.x)
]
[ TypedSvg.path
[ d <|
("M " ++ String.fromFloat (cloud.width / 2 * cloud.corners.topLeft.horizontalLength) ++ ",0")
++ (" H " ++ String.fromFloat (cloud.width - (cloud.width / 2 * cloud.corners.topRight.horizontalLength)))
++ " "
++ curve
( cloud.width - (cloud.width / 2 * cloud.corners.topRight.horizontalLength * cloud.corners.topRight.firstControlPointX)
, 0
)
( cloud.width
, rowHeight / 2 * cloud.corners.topRight.secondControlPointY
)
( cloud.width
, rowHeight / 2
)
++ curve
( cloud.width
, rowHeight / 2 + (rowHeight / 2 * cloud.corners.bottomRight.secondControlPointY)
)
( cloud.width - (cloud.width / 2 * cloud.corners.bottomRight.horizontalLength * cloud.corners.bottomRight.firstControlPointX)
, rowHeight
)
( cloud.width - (cloud.width / 2 * cloud.corners.bottomRight.horizontalLength)
, rowHeight
)
++ " H "
++ String.fromFloat (cloud.width / 2 * cloud.corners.bottomLeft.horizontalLength)
++ curve
( cloud.width / 2 * cloud.corners.bottomLeft.horizontalLength * cloud.corners.bottomLeft.firstControlPointX
, rowHeight
)
( 0
, rowHeight - (rowHeight / 2 * cloud.corners.bottomLeft.secondControlPointY)
)
( 0
, rowHeight / 2
)
++ curve
( 0
, rowHeight / 2 * cloud.corners.topLeft.secondControlPointY
)
( cloud.width / 2 * cloud.corners.topLeft.horizontalLength * cloud.corners.topLeft.firstControlPointX
, 0
)
( cloud.width / 2 * cloud.corners.topLeft.horizontalLength
, 0
)
++ " Z"
, fill (Fill Color.white)
]
[]
]
curve : ( Float, Float ) -> ( Float, Float ) -> ( Float, Float ) -> String
curve ( x1, y1 ) ( x2, y2 ) ( x, y ) =
"C "
++ String.fromFloat x1
++ " "
++ String.fromFloat y1
++ ","
++ String.fromFloat x2
++ " "
++ String.fromFloat y2
++ ","
++ String.fromFloat x
++ " "
++ String.fromFloat y

146
src/Clouds/Model.elm Normal file
View File

@ -0,0 +1,146 @@
module Clouds.Model exposing (Cloud, CloudRow, Coords, Dimensions, Model, buildCloudRow, init)
import List.Extra as List
import Messages exposing (Message)
import Random
import Time exposing (Posix)
type alias Model =
{ window : Dimensions
, cloudRows : List CloudRow
}
type alias CloudRow =
{ height : Float
, y : Float
, scale : Float
, clouds : List Cloud
, opacity : Float
}
type alias Cloud =
{ width : Float
, x : Float
, corners :
{ topLeft : CornerInfo
, topRight : CornerInfo
, bottomLeft : CornerInfo
, bottomRight : CornerInfo
}
}
type alias CornerInfo =
{ horizontalLength : Float
, firstControlPointX : Float
, secondControlPointY : Float
}
type alias Dimensions =
{ width : Float, height : Float }
type alias Coords =
{ x : Float, y : Float }
type alias Flags =
{ window : Dimensions
, time : Int
}
init : Flags -> ( Model, Cmd Message )
init flags =
let
seed =
Random.initialSeed flags.time
width =
flags.window.width - 200
in
( { window = flags.window
, cloudRows =
[ buildCloudRow width seed
]
}
, Cmd.none
)
type alias CloudBuildData =
{ totalWidth : Float
, allocatedWidth : Float
, seed : Random.Seed
}
buildCloudRow : Float -> Random.Seed -> CloudRow
buildCloudRow windowWidth seed =
{ height = 10
, y = 1
, scale = 1
, opacity = 0
, clouds =
List.unfoldr buildCloud
{ totalWidth = windowWidth
, allocatedWidth = 0
, seed = seed
}
}
buildCloud : CloudBuildData -> Maybe ( Cloud, CloudBuildData )
buildCloud { totalWidth, allocatedWidth, seed } =
if allocatedWidth >= totalWidth then
Nothing
else
let
( cloudWidth, seed1 ) =
Random.step (Random.float 20 50) seed
( topLeftCorner, seed2 ) =
Random.step cornerGenerator seed1
( topRightCorner, seed3 ) =
Random.step cornerGenerator seed2
( bottomLeftCorner, seed4 ) =
Random.step cornerGenerator seed3
( bottomRightCorner, seed5 ) =
Random.step cornerGenerator seed4
in
Just
( { width = cloudWidth
, x = allocatedWidth
, corners =
{ topLeft = topLeftCorner
, topRight = topRightCorner
, bottomLeft = bottomLeftCorner
, bottomRight = bottomRightCorner
}
}
, { allocatedWidth = allocatedWidth + cloudWidth + 5
, seed = seed5
, totalWidth = totalWidth
}
)
probability : Random.Generator Float
probability =
Random.float 0 1
cornerGenerator : Random.Generator CornerInfo
cornerGenerator =
Random.map3 CornerInfo
(Random.float 0.3 0.8)
(Random.float 0.1 0.9)
(Random.float 0.1 0.9)

53
src/Clouds/Update.elm Normal file
View File

@ -0,0 +1,53 @@
module Clouds.Update exposing (tick, updateCloudRow)
import Clouds.Model exposing (CloudRow, Model)
import Random
import Time exposing (Posix, posixToMillis)
tick : Posix -> Model -> Model
tick time model =
let
seed0 =
Random.initialSeed (posixToMillis time)
newCloudRows =
case List.head model.cloudRows of
Just firstRow ->
if firstRow.y >= 20 then
[ Clouds.Model.buildCloudRow model.window.width seed0
]
++ model.cloudRows
else
model.cloudRows
Nothing ->
-- this should never happen
model.cloudRows
in
{ model
| cloudRows =
List.map (updateCloudRow model.window.height) newCloudRows
|> List.filterMap identity
}
updateCloudRow : Float -> CloudRow -> Maybe CloudRow
updateCloudRow windowHeight row =
if row.y >= (windowHeight - 230) then
Nothing
else
Just
{ row
| y = (row.y + 0.2) * 1.0005
, height = row.height + 0.012
, scale = (row.scale + 0.00002) * 1.00002
, opacity =
if row.opacity >= 1 then
1
else
row.opacity + 0.0008
}

6
src/Interop.elm Normal file
View File

@ -0,0 +1,6 @@
port module Interop exposing (midiInput)
import Json.Decode as Json
port midiInput : (Json.Value -> msg) -> Sub msg

55
src/Main.elm Normal file
View File

@ -0,0 +1,55 @@
module Main exposing (main)
import Browser
import Browser.Events exposing (onAnimationFrame)
import Circle2d as Circle
import Clouds.Model exposing (..)
import Clouds.Update
import Interop
import List.Extra as List
import Messages exposing (..)
import Point2d as Point
import Random
import Random.Extra as Random
import Time exposing (posixToMillis)
import View
title =
"O'Keefe Clouds"
main =
Browser.document
{ init = init
, view =
\model ->
{ title = title
, body = View.draw model
}
, update = update
, subscriptions = subscriptions
}
update : Message -> Model -> ( Model, Cmd Message )
update message model =
case message of
MidiInputReceived val ->
( model, Cmd.none )
AnimationFrameTriggered time ->
( Clouds.Update.tick time model, Cmd.none )
ModifierChanged mod val ->
( model
, Cmd.none
)
subscriptions : Model -> Sub Message
subscriptions model =
Sub.batch
[ onAnimationFrame AnimationFrameTriggered
, Interop.midiInput MidiInputReceived
]

14
src/Messages.elm Normal file
View File

@ -0,0 +1,14 @@
module Messages exposing (Message(..), Modifier(..))
import Json.Decode as Json
import Time exposing (Posix)
type Modifier
= None
type Message
= AnimationFrameTriggered Posix
| ModifierChanged Modifier Float
| MidiInputReceived Json.Value

55
src/View.elm Normal file
View File

@ -0,0 +1,55 @@
module View exposing (draw)
import Clouds.EffectView
import Clouds.Model exposing (Model)
import Element exposing (..)
import Element.Background as Background
import Element.Border as Border
import Element.Input as Input
import Html exposing (Html)
import Messages exposing (..)
mods =
[]
draw : Model -> List (Html Message)
draw model =
[ layout [ width fill, height fill ] <|
row [ width fill, height fill ]
[ column [ width (px 200), height fill, spacing 50, padding 50 ] <|
List.map (modSlider model) mods
, el [ width fill, height fill ] <|
html (Clouds.EffectView.draw model)
]
]
modSlider model ( modifier, label, prop ) =
el
[ height (px 50)
, width (px 150)
, centerX
]
<|
Input.slider
[ Element.behindContent
(Element.el
[ Element.width Element.fill
, Element.height (Element.px 2)
, Element.centerY
, Background.color (rgb 0.5 0.5 0.5)
, Border.rounded 2
]
Element.none
)
]
{ onChange = ModifierChanged modifier
, label = Input.labelAbove [] (text label)
, min = 0
, max = 1
, value = prop model
, thumb = Input.defaultThumb
, step = Just 0.01
}