├── .github
└── workflows
│ ├── Makefile
│ ├── build-and-test.jsonnet
│ ├── build-and-test.yml
│ ├── jsonnet_to_yaml.sh
│ └── semgrep.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── core
├── Basics.ml
├── Color.ml
├── Keyboard.ml
├── Set.ml
├── Set_.ml
├── Set_.mli
├── Time.ml
└── dune
├── docs
├── elm_core
│ ├── Basics
│ │ └── index.html
│ ├── Color
│ │ └── index.html
│ ├── Keyboard
│ │ └── index.html
│ ├── Set
│ │ └── index.html
│ ├── Set_
│ │ └── index.html
│ ├── Time
│ │ └── index.html
│ └── index.html
├── elm_playground
│ ├── Playground
│ │ └── index.html
│ ├── Playground_platform
│ │ └── index.html
│ └── index.html
├── elm_playground_native
│ └── index.html
├── elm_playground_web
│ └── index.html
├── elm_system
│ ├── Cmd
│ │ └── index.html
│ ├── Sub
│ │ └── index.html
│ └── index.html
├── examples
│ ├── Animation.bc.js
│ ├── Animation.html
│ ├── Keyboard.bc.js
│ ├── Keyboard.html
│ ├── Mario.bc.js
│ ├── Mario.html
│ ├── Mouse.bc.js
│ ├── Mouse.html
│ ├── Picture.bc.js
│ ├── Picture.html
│ ├── Smiley.bc.js
│ ├── Smiley.html
│ ├── Turtle.bc.js
│ ├── Turtle.html
│ ├── Words.bc.js
│ ├── Words.html
│ └── index.html
├── games
│ ├── Asteroid.bc.js
│ ├── Asteroid.html
│ ├── Pong.bc.js
│ ├── Pong.html
│ ├── Snake.bc.js
│ ├── Snake.html
│ ├── Template.html
│ ├── Tetris.bc.js
│ ├── Tetris.html
│ └── index.html
├── index.html
├── odoc.support
│ ├── fonts
│ │ ├── KaTeX_AMS-Regular.woff2
│ │ ├── KaTeX_Caligraphic-Bold.woff2
│ │ ├── KaTeX_Caligraphic-Regular.woff2
│ │ ├── KaTeX_Fraktur-Bold.woff2
│ │ ├── KaTeX_Fraktur-Regular.woff2
│ │ ├── KaTeX_Main-Bold.woff2
│ │ ├── KaTeX_Main-BoldItalic.woff2
│ │ ├── KaTeX_Main-Italic.woff2
│ │ ├── KaTeX_Main-Regular.woff2
│ │ ├── KaTeX_Math-BoldItalic.woff2
│ │ ├── KaTeX_Math-Italic.woff2
│ │ ├── KaTeX_SansSerif-Bold.woff2
│ │ ├── KaTeX_SansSerif-Italic.woff2
│ │ ├── KaTeX_SansSerif-Regular.woff2
│ │ ├── KaTeX_Script-Regular.woff2
│ │ ├── KaTeX_Size1-Regular.woff2
│ │ ├── KaTeX_Size2-Regular.woff2
│ │ ├── KaTeX_Size3-Regular.woff2
│ │ ├── KaTeX_Size4-Regular.woff2
│ │ ├── KaTeX_Typewriter-Regular.woff2
│ │ ├── fira-mono-v14-latin-500.woff2
│ │ ├── fira-mono-v14-latin-regular.woff2
│ │ ├── fira-sans-v17-latin-500.woff2
│ │ ├── fira-sans-v17-latin-500italic.woff2
│ │ ├── fira-sans-v17-latin-700.woff2
│ │ ├── fira-sans-v17-latin-700italic.woff2
│ │ ├── fira-sans-v17-latin-italic.woff2
│ │ ├── fira-sans-v17-latin-regular.woff2
│ │ ├── noticia-text-v15-latin-700.woff2
│ │ ├── noticia-text-v15-latin-italic.woff2
│ │ └── noticia-text-v15-latin-regular.woff2
│ ├── highlight.pack.js
│ ├── katex.min.css
│ ├── katex.min.js
│ ├── odoc.css
│ └── odoc_search.js
├── screenshots
│ ├── example-animation.png
│ ├── example-keyboard.png
│ ├── example-misc.png
│ ├── example-mouse.png
│ ├── example-picture.png
│ ├── example-smiley.png
│ ├── example-turtle.png
│ ├── example-words.png
│ ├── game-asteroid.png
│ ├── game-pong.png
│ ├── game-snake.png
│ ├── game-tetris.png
│ ├── keyboard-game-native.png
│ └── keyboard-game-start-native.png
├── toy-native-example
│ ├── Makefile
│ ├── README.md
│ ├── Toy.ml
│ ├── dune
│ ├── dune-project
│ └── toy-game.opam
└── toy-web-example
│ ├── Makefile
│ ├── README.md
│ ├── Toy.ml
│ ├── dune
│ ├── dune-project
│ ├── static
│ ├── Toy.bc.js
│ └── Toy.html
│ └── toy-web-game.opam
├── dune
├── dune-project
├── elm_core.opam
├── elm_playground.opam
├── elm_playground_native.opam
├── elm_playground_native.opam.template
├── elm_playground_web.opam
├── elm_system.opam
├── examples
├── Animation.ml
├── Keyboard.ml
├── Mario.ml
├── Misc.ml
├── Mouse.ml
├── Picture.ml
├── Smiley.ml
├── Turtle.ml
├── Words.ml
└── dune
├── examples_js
├── Animation.html
├── Animation.ml
├── Keyboard.html
├── Keyboard.ml
├── Mario.html
├── Mario.ml
├── Mouse.html
├── Mouse.ml
├── Picture.html
├── Picture.ml
├── Smiley.html
├── Smiley.ml
├── Template.html
├── Turtle.html
├── Turtle.ml
├── Words.html
├── Words.ml
├── dune
└── test.html
├── games
├── Asteroid.ml
├── Pong.ml
├── Snake.ml
├── Tetris.ml
├── dune
└── template.ml
├── games_js
├── Asteroid.html
├── Asteroid.ml
├── Pong.html
├── Pong.ml
├── Snake.html
├── Snake.ml
├── Template.html
├── Tetris.html
├── Tetris.ml
└── dune
├── playground
├── Playground.ml
├── Playground.mli
├── Playground_platform.mli
├── dune
├── index.mld
├── native
│ ├── Playground_platform.ml
│ └── dune
└── web
│ ├── Playground_platform.ml
│ └── dune
├── semgrep.jsonnet
├── skip_list.txt
├── system
├── Cmd.ml
├── Sub.ml
├── dune
└── pad.txt
└── tests
├── Svg.html
├── Svg.ml
├── Test_cairo_graphics.ml
├── Test_ocamlsdl2.ml
├── Test_tsdl.ml
├── Test_vdom.html
├── Test_vdom.ml
└── dune
/.github/workflows/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile to automatically convert the .jsonnet GHA workflows
2 | # to .yml (GHA accepts only the YAML format).
3 |
4 | LIBS =
5 | OBJS = build-and-test.yml
6 |
7 | all: $(OBJS)
8 |
9 | %.yml: %.jsonnet $(LIBS)
10 | echo "# AUTOGENERATED FROM $< DO NOT MODIFY" > $@
11 | ./jsonnet_to_yaml.sh $< >> $@ || { rm -f $@; exit 1; }
12 |
13 | clean:
14 | rm -f $(OBJS)
15 |
16 | format:
17 | jsonnetfmt -i *.jsonnet
18 |
19 | # for pad's codemap
20 | .PHONY: gen-codemapignore
21 | gen-codemapignore: $(OBJS)
22 | rm -f .codemapignore
23 | for i in $(OBJS); do \
24 | echo $$i >> .codemapignore; \
25 | done
26 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.jsonnet:
--------------------------------------------------------------------------------
1 | // Build and test using setup-ocaml@v3 mostly.
2 |
3 | // ----------------------------------------------------------------------------
4 | // Helpers
5 | // ----------------------------------------------------------------------------
6 |
7 | local checkout = {
8 | uses: 'actions/checkout@v3',
9 | };
10 |
11 | // ----------------------------------------------------------------------------
12 | // The job
13 | // ----------------------------------------------------------------------------
14 |
15 | local job = {
16 | strategy: {
17 | matrix: {
18 | 'os': [
19 | 'ubuntu-latest',
20 | 'macos-latest',
21 | // does not work because of tsdl compilation error on windows
22 | //'windows-latest'
23 | ],
24 | 'ocaml-compiler': [
25 | '4.14.1',
26 | '5.2.0',
27 | ],
28 | },
29 | //'fail-fast': false,
30 | },
31 | 'runs-on': '${{ matrix.os }}',
32 | steps: [
33 | checkout,
34 | {
35 | uses: 'ocaml/setup-ocaml@v3',
36 | with: {
37 | 'ocaml-compiler': '${{ matrix.ocaml-compiler }}',
38 | // I used to have "'opam-depext': false" below, but this flag
39 | // is not supported in setup@v3 and anyway we do want
40 | // opam-depext otherwise the installation would fail
41 | // because some packages like ocurl have external
42 | // dependencies (depext) and require some system packages
43 | // to be installed. opam-depext does this for us automatically
44 | // and in a portable way (for linux/mac/windows)!.
45 | },
46 | },
47 | {
48 | name: 'Install dependencies',
49 | run: |||
50 | opam install --deps-only .
51 | |||,
52 | },
53 | {
54 | name: 'Build',
55 | run: |||
56 | eval $(opam env)
57 | make
58 | |||,
59 | },
60 | {
61 | name: 'Test',
62 | run: |||
63 | eval $(opam env)
64 | make test
65 | |||,
66 | },
67 |
68 | ],
69 | };
70 |
71 | // ----------------------------------------------------------------------------
72 | // The workflow
73 | // ----------------------------------------------------------------------------
74 | {
75 | name: 'build-and-test',
76 | on: {
77 | // can be run manually from the GHA dashboard
78 | workflow_dispatch: null,
79 | // on the PR
80 | pull_request: null,
81 | // and another time once the PR is merged on master
82 | push: {
83 | branches: [
84 | 'master',
85 | ],
86 | },
87 | // disabled the cron for now because github complains after
88 | // a few months of inactivity when there is no new commit
89 | // but the crons ran many times, so simpler to disable for now
90 | //schedule: [
91 | // {
92 | // // every day at 12:59
93 | // cron: '59 12 * * *',
94 | // },
95 | //],
96 | },
97 | jobs: {
98 | job: job,
99 | },
100 | }
101 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | # AUTOGENERATED FROM build-and-test.jsonnet DO NOT MODIFY
2 | jobs:
3 | job:
4 | runs-on: ${{ matrix.os }}
5 | steps:
6 | - uses: actions/checkout@v3
7 | - uses: ocaml/setup-ocaml@v3
8 | with:
9 | ocaml-compiler: ${{ matrix.ocaml-compiler }}
10 | - name: Install dependencies
11 | run: |
12 | opam install --deps-only .
13 | - name: Build
14 | run: |
15 | eval $(opam env)
16 | make
17 | - name: Test
18 | run: |
19 | eval $(opam env)
20 | make test
21 | strategy:
22 | matrix:
23 | ocaml-compiler:
24 | - 4.14.1
25 | - 5.2.0
26 | os:
27 | - ubuntu-latest
28 | - macos-latest
29 | name: build-and-test
30 | on:
31 | pull_request: null
32 | push:
33 | branches:
34 | - master
35 | workflow_dispatch: null
36 |
--------------------------------------------------------------------------------
/.github/workflows/jsonnet_to_yaml.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 |
5 | # This script requires to have:
6 | # - jsonnet (any jsonnet should work, e.g., go-jsonnet)
7 | # - yq (a.k.a go-yq) from https://github.com/mikefarah/yq
8 | # Note that many distributions come with a default yq that is not
9 | # as powerful as https://github.com/mikefarah/yq so follow
10 | # the instructions there to install yq on your machine
11 | #
12 | # The sed command is because 'on' is printed with or without quotes depending
13 | # on the version. It's a dirty hack that may break some input.
14 |
15 | jsonnet "$@" \
16 | | yq eval -P \
17 | | sed -e 's/^\( *\)"on":/\1on:/' \
18 | | sed -e 's/: "yes"$/: yes/'
19 |
--------------------------------------------------------------------------------
/.github/workflows/semgrep.yml:
--------------------------------------------------------------------------------
1 | jobs:
2 | job:
3 | runs-on: ubuntu-20.04
4 | container:
5 | # living on the edge!
6 | image: semgrep/semgrep:pro-develop
7 | #TODO: at some point
8 | #env:
9 | # SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
10 | # ...
11 | # run: semgrep ci
12 | steps:
13 | - uses: actions/checkout@v3
14 | # TODO: not sure why we need that, the docker contain should do
15 | # similar things with ~root/.gitconfig
16 | - run: git config --global --add safe.directory /__w/ocaml-elm-playground/ocaml-elm-playground
17 | # coupling: with Makefile 'make check' target
18 | - run: semgrep scan --experimental --config semgrep.jsonnet --strict --error --debug
19 |
20 | name: semgrep
21 | on:
22 | # old was pull_request_target but has weird effect like not using
23 | # the latest change in this file
24 | pull_request: null
25 | workflow_dispatch: null
26 | push:
27 | branches:
28 | - master
29 | #schedule:
30 | # - cron: 50 15 * * *
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _build/
2 | .merlin
3 |
4 | todo.txt
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.2.1 (2024-12-24)
4 |
5 | First announce at https://discuss.ocaml.org/t/ann-first-release-of-elm-playground/15838
6 | and a better README at https://github.com/aryx/ocaml-elm-playground
7 |
8 | ## 0.2.0 (2024-12-13)
9 |
10 | First website at https://aryx.github.io/ocaml-elm-playground/
11 |
12 | ## 0.1.0 (2024-12-8)
13 |
14 | First release on OPAM (actually 0.1.7 was the version released).
15 |
16 | ## Beta (2021-01-05)
17 |
18 | Initial native (via TSDL) and web (via vdom) "backends"
19 | and a few basic games (Pong, Asteroid, Snake, and even Tetris!)
20 | working in native and a few working in web.
21 |
22 | ## Alpha (2020-11-02)
23 |
24 | First commits and first prototype.
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019-present, Evan Czaplicki. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
8 |
9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Prelude
3 | ###############################################################################
4 |
5 | ###############################################################################
6 | # Main targets
7 | ###############################################################################
8 |
9 | OPAMS=\
10 | elm_core.opam elm_system.opam\
11 | elm_playground.opam elm_playground_native.opam elm_playground_web.opam
12 |
13 | default: all
14 |
15 | all: $(OPAMS)
16 | dune build
17 | clean:
18 | dune clean
19 | install:
20 | dune install
21 |
22 | test:
23 | dune runtest -f
24 |
25 | # This will fail if the .opam isn't up-to-date (in git),
26 | # and dune isn't installed yet. You can always install dune
27 | # with 'opam install dune' to get started.
28 | %.opam: dune-project
29 | dune build $@
30 |
31 | ###############################################################################
32 | # Release
33 | ###############################################################################
34 |
35 | ###############################################################################
36 | # Website building
37 | ###############################################################################
38 |
39 | doc:
40 | dune build @doc
41 |
42 | # Note that I've configured Github Pages for this project at
43 | # https://github.com/aryx/ocaml-elm-playground/settings/pages
44 | # I've selected "Deploy from Branch" "master" and "/docs"
45 | # so it assumes all the html are under docs/.
46 | # Note that if you change the settings, you need to commit in
47 | # the master branch to trigger a redeploy
48 | # TODO: automatically update games/ and examples/
49 | # and add entries for those dirs.
50 | website:
51 | rm -rf docs
52 | make doc
53 | cp -a _build/default/_doc/_html docs
54 | make js
55 |
56 | # Preview the site at http://localhost:8000
57 | serve:
58 | python -m http.server --directory docs 8000
59 |
60 | js:
61 | dune build games_js --profile=release-js
62 | dune build examples_js --profile=release-js
63 |
64 | ###############################################################################
65 | # Developer targets
66 | ###############################################################################
67 |
68 | check:
69 | ~/zz/bin/osemgrep --experimental --config semgrep.jsonnet .
70 |
71 | # To bump-version you need to modify dune-project version then run 'make' then
72 | # commit and merge then:
73 | # git tag -a 0.1.8
74 | # git push origin 0.1.8
75 | # opam publish
76 | # and that's it!
77 | bump:
78 | echo TODO, see the comment in this file
79 |
80 | pr:
81 | git push origin `git rev-parse --abbrev-ref HEAD`
82 | hub pull-request -b master
83 | push:
84 | git push origin `git rev-parse --abbrev-ref HEAD`
85 | merge:
86 | A=`git rev-parse --abbrev-ref HEAD` && git checkout master && git pull && git branch -D $$A
87 |
88 | visual:
89 | codemap -screen_size 3 -filter pfff -efuns_client efuns_client -emacs_client /dev/null .
90 |
91 | opendoc:
92 | dune build @doc
93 | open _build/default/_doc/_html/index.html
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OCaml Elm Playground
2 | =======================
3 |
4 | Create pictures, animations, and video games with OCaml!
5 |
6 | This is a port of the excellent Elm playground package
7 | https://github.com/evancz/elm-playground to OCaml.
8 |
9 | > This is the package I wanted when I was learning programming. Start by
10 | > putting shapes on screen and work up to making games. I hope this
11 | > package will be fun for a broad range of ages and backgrounds!
12 | > *- Evan Czaplicki*
13 |
14 |
15 | Documentation
16 | ---------------------------------------------------
17 |
18 | * [Getting started](https://github.com/aryx/ocaml-elm-playground?tab=readme-ov-file#ocaml-elm-playground) (this file)
19 | * [Tutorial](https://aryx.github.io/ocaml-elm-playground/elm_playground/)
20 | * [Basic examples](https://aryx.github.io/ocaml-elm-playground/examples/)
21 | * [Basic games](https://aryx.github.io/ocaml-elm-playground/games/)
22 | * [API reference](https://aryx.github.io/ocaml-elm-playground/elm_playground/Playground/)
23 | * [Index](https://aryx.github.io/ocaml-elm-playground)
24 |
25 | Features
26 | --------------
27 |
28 | The OCaml `elm_playground` package allows you to easily create
29 | *pictures*, *animations*, and even *video games* in a portable way using an API that
30 | really simplifies how to view the computer and its devices (the screen,
31 | keyboard, and mouse).
32 |
33 | The goal is similar to the old [`graphics` package](https://github.com/ocaml/graphics)
34 | but goes even further in terms of simplification.
35 |
36 | The main API is defined in a single
37 | [Playground.mli](https://github.com/aryx/ocaml-elm-playground/blob/master/playground/Playground.mli) module and is implemented by two backends:
38 | - a *native* (SDL-based) backend to run your game on your desktop from a terminal
39 | - a *web* (vdom-based) backend to run your game in a browser
40 |
41 | Here is for example a simple [Snake game](https://aryx.github.io/ocaml-elm-playground/games/Snake.html) you can run from your browser (use the arrow keys to change the direction of the snake and eat the ball to grow your length). You can run the same game
42 | on your desktop *without changing a line of code*.
43 |
44 | Install
45 | --------------
46 |
47 | To install the playground, run `opam install elm_playground` and then
48 | install one or both backends with `opam install elm_playground_native`
49 | and/or `opam install elm_playground_web`.
50 |
51 | Simple native application
52 | --------------------------
53 |
54 | Here is a very simple application using the playground:
55 | ```ocaml
56 | open Playground
57 |
58 | (* the (x, y) position of the blue square *)
59 | type model = (float * float)
60 |
61 | let initial_state : model = (0., 0.)
62 |
63 | let view _computer (x, y) = [
64 | square blue 40.
65 | |> move x y
66 | ]
67 |
68 | let update computer (x, y) =
69 | (x +. to_x computer.keyboard, y +. to_y computer.keyboard)
70 |
71 | let app =
72 | game view update initial_state
73 |
74 | let main = Playground_platform.run_app app
75 | ```
76 |
77 |
78 | It is a very simple `game` defining a `model` type, a `view` function, and an `update` function to specify how the game behaves. It is using the
79 | "Model-View-Update" architecture to organize the code of a graphical
80 | interactive application
81 | (see https://guide.elm-lang.org/architecture/ for more information).
82 |
83 | To compile this application, simply do:
84 | ```bash
85 | $ cd docs/toy-native-example
86 | $ opam install --deps-only --yes .
87 | $ dune exec --root . ./Toy.exe
88 | ```
89 | You should then see on your desktop:
90 |
91 |
93 |
94 | If you type on the arrow keys on your keyboard the blue square should move in the
95 | corresponding direction. If you type `q` it will exit the game.
96 |
97 | Note that with the Playground API the center of the screen is at `(0, 0)`.
98 |
99 | Simple web application
100 | --------------------------
101 |
102 | To compile this same application for the web, simply do:
103 |
104 | ```bash
105 | $ cd docs/toy-web-example
106 | $ opam install --deps-only --yes .
107 | $ dune build --root .
108 | $ cp _build/default/Toy.bc.js static/
109 | ```
110 | You should then be able to use the web app by going to:
111 | https://aryx.github.io/ocaml-elm-playground/toy-web-example/static/Toy.html
112 |
113 | By default the generated javascript file can be big so to get a smaller one
114 | you can do instead:
115 | ```bash
116 | $ dune build --root . --profile=release
117 | $ cp _build/default/Toy.bc.js static/
118 | ```
119 |
120 | Next steps
121 | ------------
122 |
123 | Read the tutorial at:
124 | https://aryx.github.io/ocaml-elm-playground/elm_playground/index.html
125 |
126 | Look at the code under [examples/](examples/) and [games/](games/).
127 |
128 | Here is a screenshot of the [Tetris](games/Tetris.ml) Playgound game running:
129 |
131 |
132 | You can see a few more screenshots [here](docs/screenshots/).
133 |
134 | Limitations
135 | ------------
136 |
137 | The web backend is not fully finished yet. Many of the examples and games
138 | do not work fully yet on the web. You've been warned.
139 | Contributions are welcome!
140 |
--------------------------------------------------------------------------------
/core/Basics.ml:
--------------------------------------------------------------------------------
1 | let log s =
2 | Printf.printf "%s" s
3 |
4 | let (/..) = (/)
5 | let (+..) = (+)
6 | let (-..) = (-)
7 | let ( *.. ) = ( * )
8 |
9 | (* prefer float operators as default *)
10 | let (/) = (/.)
11 | let (+) = (+.)
12 | let (-) = (-.)
13 | let ( * ) = ( *. )
14 |
15 | let (round: float -> int) = fun f ->
16 | int_of_float (floor (f +. 0.5))
17 |
18 | let mod_by a b =
19 | b mod a
20 |
21 | let pi = Float.pi
22 | let pi2 = 8. *. atan 1.
23 |
24 | (* was called just degrees in Basics.elm *)
25 | let degrees_to_radians deg =
26 | (deg * pi) / 180.
27 |
28 | let radians_to_degrees rad =
29 | rad * 180. / pi
30 |
31 | let (turns: float -> float) = fun angle_in_turns ->
32 | 2. * pi * angle_in_turns
33 |
34 | let (clamp: 'number -> 'number -> 'number -> 'number) = fun low high number ->
35 | if number < low then
36 | low
37 | else if number > high then
38 | high
39 | else
40 | number
41 |
--------------------------------------------------------------------------------
/core/Color.ml:
--------------------------------------------------------------------------------
1 | type t =
2 | | Hex of string
3 | | Rgb of int * int * int
4 |
5 | let (color_clamp : int -> int) = fun number ->
6 | Basics.clamp 0 255 number
7 |
8 | let (rgb : int -> int -> int -> t) = fun r g b ->
9 | Rgb (color_clamp r, color_clamp g, color_clamp b)
10 |
11 |
12 | let white = Hex "#FFFFFF"
13 | let black = Hex "#000000"
14 |
15 | let red = Hex "#cc0000"
16 | let green = Hex "#73d216"
17 | let blue = Hex "#3465a4"
18 |
19 | let yellow = Hex "#edd400"
20 |
21 | let brown = Hex "#c17d11"
22 |
23 | (*-------------------------------------------------------------------*)
24 | (* Light colors *)
25 | (*-------------------------------------------------------------------*)
26 |
27 | let lightYellow = Hex "#fce94f"
28 | let lightPurple = Hex "#ad7fa8"
29 |
30 | (*-------------------------------------------------------------------*)
31 | (* Dark colors *)
32 | (*-------------------------------------------------------------------*)
33 |
34 | (*-------------------------------------------------------------------*)
35 | (* Shades of grey *)
36 | (*-------------------------------------------------------------------*)
37 |
38 | let gray = Hex "#d3d7cf"
39 |
40 | let darkGray = Hex "#babdb6"
41 |
--------------------------------------------------------------------------------
/core/Keyboard.ml:
--------------------------------------------------------------------------------
1 |
2 | type key = string
3 |
--------------------------------------------------------------------------------
/core/Set.ml:
--------------------------------------------------------------------------------
1 | type 'a t = 'a Set_.t
2 | let empty = Set_.empty
3 | let insert = Set_.add
4 | let remove = Set_.remove
5 |
--------------------------------------------------------------------------------
/core/Set_.ml:
--------------------------------------------------------------------------------
1 | (*pad: taken from set.ml from stdlib ocaml, functor sux: module Make(Ord: OrderedType) = *)
2 | (* with some addons such as from list *)
3 |
4 | (***********************************************************************)
5 | (* *)
6 | (* Objective Caml *)
7 | (* *)
8 | (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
9 | (* *)
10 | (* Copyright 1996 Institut National de Recherche en Informatique et *)
11 | (* en Automatique. All rights reserved. This file is distributed *)
12 | (* under the terms of the GNU Library General Public License, with *)
13 | (* the special exception on linking described in file ../LICENSE. *)
14 | (* *)
15 | (***********************************************************************)
16 |
17 | (* set.ml 1.18.4.1 2004/11/03 21:19:49 doligez Exp *)
18 |
19 | (* Sets over ordered types *)
20 |
21 | (* pad:
22 | type elt = Ord.t
23 | type t = Empty | Node of t * elt * t * int
24 | and subst all Ord.compare with just compare
25 | *)
26 | type 'elt t = Empty | Node of 'elt t * 'elt * 'elt t * int
27 |
28 | (* Sets are represented by balanced binary trees (the heights of the
29 | children differ by at most 2 *)
30 |
31 | let height = function
32 | | Empty -> 0
33 | | Node (_, _, _, h) -> h
34 |
35 | (* Creates a new node with left son l, value v and right son r.
36 | We must have all elements of l < v < all elements of r.
37 | l and r must be balanced and | height l - height r | <= 2.
38 | Inline expansion of height for better speed. *)
39 |
40 | let create l v r =
41 | let hl =
42 | match l with
43 | | Empty -> 0
44 | | Node (_, _, _, h) -> h
45 | in
46 | let hr =
47 | match r with
48 | | Empty -> 0
49 | | Node (_, _, _, h) -> h
50 | in
51 | Node (l, v, r, if hl >= hr then hl + 1 else hr + 1)
52 |
53 | (* Same as create, but performs one step of rebalancing if necessary.
54 | Assumes l and r balanced and | height l - height r | <= 3.
55 | Inline expansion of create for better speed in the most frequent case
56 | where no rebalancing is required. *)
57 |
58 | let bal l v r =
59 | let hl =
60 | match l with
61 | | Empty -> 0
62 | | Node (_, _, _, h) -> h
63 | in
64 | let hr =
65 | match r with
66 | | Empty -> 0
67 | | Node (_, _, _, h) -> h
68 | in
69 | if hl > hr + 2 then
70 | match l with
71 | | Empty -> invalid_arg "Set.bal"
72 | | Node (ll, lv, lr, _) -> (
73 | if height ll >= height lr then create ll lv (create lr v r)
74 | else
75 | match lr with
76 | | Empty -> invalid_arg "Set.bal"
77 | | Node (lrl, lrv, lrr, _) ->
78 | create (create ll lv lrl) lrv (create lrr v r))
79 | else if hr > hl + 2 then
80 | match r with
81 | | Empty -> invalid_arg "Set.bal"
82 | | Node (rl, rv, rr, _) -> (
83 | if height rr >= height rl then create (create l v rl) rv rr
84 | else
85 | match rl with
86 | | Empty -> invalid_arg "Set.bal"
87 | | Node (rll, rlv, rlr, _) ->
88 | create (create l v rll) rlv (create rlr rv rr))
89 | else Node (l, v, r, if hl >= hr then hl + 1 else hr + 1)
90 |
91 | (* Insertion of one element *)
92 |
93 | let rec add x = function
94 | | Empty -> Node (Empty, x, Empty, 1)
95 | | Node (l, v, r, _) as t ->
96 | let c = compare x v in
97 | if c = 0 then t
98 | else if c < 0 then bal (add x l) v r
99 | else bal l v (add x r)
100 |
101 | (* Same as create and bal, but no assumptions are made on the
102 | relative heights of l and r. *)
103 |
104 | let rec join l v r =
105 | match (l, r) with
106 | | Empty, _ -> add v r
107 | | _, Empty -> add v l
108 | | Node (ll, lv, lr, lh), Node (rl, rv, rr, rh) ->
109 | if lh > rh + 2 then bal ll lv (join lr v r)
110 | else if rh > lh + 2 then bal (join l v rl) rv rr
111 | else create l v r
112 |
113 | (* Smallest and greatest element of a set *)
114 |
115 | let rec min_elt = function
116 | | Empty -> raise Not_found
117 | | Node (Empty, v, _r, _) -> v
118 | | Node (l, _v, _r, _) -> min_elt l
119 |
120 | let rec max_elt = function
121 | | Empty -> raise Not_found
122 | | Node (_l, v, Empty, _) -> v
123 | | Node (_l, _v, r, _) -> max_elt r
124 |
125 | (* Remove the smallest element of the given set *)
126 |
127 | let rec remove_min_elt = function
128 | | Empty -> invalid_arg "Set.remove_min_elt"
129 | | Node (Empty, _v, r, _) -> r
130 | | Node (l, v, r, _) -> bal (remove_min_elt l) v r
131 |
132 | (* Merge two trees l and r into one.
133 | All elements of l must precede the elements of r.
134 | Assume | height l - height r | <= 2. *)
135 |
136 | let merge t1 t2 =
137 | match (t1, t2) with
138 | | Empty, t -> t
139 | | t, Empty -> t
140 | | _, _ -> bal t1 (min_elt t2) (remove_min_elt t2)
141 |
142 | (* Merge two trees l and r into one.
143 | All elements of l must precede the elements of r.
144 | No assumption on the heights of l and r. *)
145 |
146 | let concat t1 t2 =
147 | match (t1, t2) with
148 | | Empty, t -> t
149 | | t, Empty -> t
150 | | _, _ -> join t1 (min_elt t2) (remove_min_elt t2)
151 |
152 | (* Splitting. split x s returns a triple (l, present, r) where
153 | - l is the set of elements of s that are < x
154 | - r is the set of elements of s that are > x
155 | - present is false if s contains no element equal to x,
156 | or true if s contains an element equal to x. *)
157 |
158 | let rec split x = function
159 | | Empty -> (Empty, false, Empty)
160 | | Node (l, v, r, _) ->
161 | let c = compare x v in
162 | if c = 0 then (l, true, r)
163 | else if c < 0 then
164 | let ll, pres, rl = split x l in
165 | (ll, pres, join rl v r)
166 | else
167 | let lr, pres, rr = split x r in
168 | (join l v lr, pres, rr)
169 |
170 | (* Implementation of the set operations *)
171 |
172 | let empty = Empty
173 |
174 | let is_empty = function
175 | | Empty -> true
176 | | _ -> false
177 |
178 | let rec mem x = function
179 | | Empty -> false
180 | | Node (l, v, r, _) ->
181 | let c = compare x v in
182 | c = 0 || mem x (if c < 0 then l else r)
183 |
184 | let singleton x = Node (Empty, x, Empty, 1)
185 |
186 | let rec remove x = function
187 | | Empty -> Empty
188 | | Node (l, v, r, _) ->
189 | let c = compare x v in
190 | if c = 0 then merge l r
191 | else if c < 0 then bal (remove x l) v r
192 | else bal l v (remove x r)
193 |
194 | let rec union s1 s2 =
195 | match (s1, s2) with
196 | | Empty, t2 -> t2
197 | | t1, Empty -> t1
198 | | Node (l1, v1, r1, h1), Node (l2, v2, r2, h2) ->
199 | if h1 >= h2 then
200 | if h2 = 1 then add v2 s1
201 | else
202 | let l2, _, r2 = split v1 s2 in
203 | join (union l1 l2) v1 (union r1 r2)
204 | else if h1 = 1 then add v1 s2
205 | else
206 | let l1, _, r1 = split v2 s1 in
207 | join (union l1 l2) v2 (union r1 r2)
208 |
209 | let rec inter s1 s2 =
210 | match (s1, s2) with
211 | | Empty, _t2 -> Empty
212 | | _t1, Empty -> Empty
213 | | Node (l1, v1, r1, _), t2 -> (
214 | match split v1 t2 with
215 | | l2, false, r2 -> concat (inter l1 l2) (inter r1 r2)
216 | | l2, true, r2 -> join (inter l1 l2) v1 (inter r1 r2))
217 |
218 | let rec diff s1 s2 =
219 | match (s1, s2) with
220 | | Empty, _t2 -> Empty
221 | | t1, Empty -> t1
222 | | Node (l1, v1, r1, _), t2 -> (
223 | match split v1 t2 with
224 | | l2, false, r2 -> join (diff l1 l2) v1 (diff r1 r2)
225 | | l2, true, r2 -> concat (diff l1 l2) (diff r1 r2))
226 |
227 | let rec compare_aux l1 l2 =
228 | match (l1, l2) with
229 | | [], [] -> 0
230 | | [], _ -> -1
231 | | _, [] -> 1
232 | | Empty :: t1, Empty :: t2 -> compare_aux t1 t2
233 | | Node (Empty, v1, r1, _) :: t1, Node (Empty, v2, r2, _) :: t2 ->
234 | let c = compare v1 v2 in
235 | if c <> 0 then c else compare_aux (r1 :: t1) (r2 :: t2)
236 | | Node (l1, v1, r1, _) :: t1, t2 ->
237 | compare_aux (l1 :: Node (Empty, v1, r1, 0) :: t1) t2
238 | | t1, Node (l2, v2, r2, _) :: t2 ->
239 | compare_aux t1 (l2 :: Node (Empty, v2, r2, 0) :: t2)
240 |
241 | let rec subset s1 s2 =
242 | match (s1, s2) with
243 | | Empty, _ -> true
244 | | _, Empty -> false
245 | | Node (l1, v1, r1, _), (Node (l2, v2, r2, _) as t2) ->
246 | let c = compare v1 v2 in
247 | if c = 0 then subset l1 l2 && subset r1 r2
248 | else if c < 0 then subset (Node (l1, v1, Empty, 0)) l2 && subset r1 t2
249 | else subset (Node (Empty, v1, r1, 0)) r2 && subset l1 t2
250 |
251 | let compare s1 s2 = compare_aux [ s1 ] [ s2 ]
252 | let equal s1 s2 = compare s1 s2 = 0
253 |
254 | let rec iter f = function
255 | | Empty -> ()
256 | | Node (l, v, r, _) ->
257 | iter f l;
258 | f v;
259 | iter f r
260 |
261 | let rec fold f s accu =
262 | match s with
263 | | Empty -> accu
264 | | Node (l, v, r, _) -> fold f l (f v (fold f r accu))
265 |
266 | let rec for_all p = function
267 | | Empty -> true
268 | | Node (l, v, r, _) -> p v && for_all p l && for_all p r
269 |
270 | let rec exists p = function
271 | | Empty -> false
272 | | Node (l, v, r, _) -> p v || exists p l || exists p r
273 |
274 | let filter p s =
275 | let rec filt accu = function
276 | | Empty -> accu
277 | | Node (l, v, r, _) -> filt (filt (if p v then add v accu else accu) l) r
278 | in
279 | filt Empty s
280 |
281 | let partition p s =
282 | let rec part ((t, f) as accu) = function
283 | | Empty -> accu
284 | | Node (l, v, r, _) ->
285 | part (part (if p v then (add v t, f) else (t, add v f)) l) r
286 | in
287 | part (Empty, Empty) s
288 |
289 | let rec cardinal = function
290 | | Empty -> 0
291 | | Node (l, _v, r, _) -> cardinal l + 1 + cardinal r
292 |
293 | let rec elements_aux accu = function
294 | | Empty -> accu
295 | | Node (l, v, r, _) -> elements_aux (v :: elements_aux accu r) l
296 |
297 | let elements s = elements_aux [] s
298 | let choose = min_elt
299 |
300 | (* pad: *)
301 | let (of_list : 'a list -> 'a t) =
302 | fun xs -> List.fold_left (fun a e -> add e a) empty xs
303 |
304 | (* martin: *)
305 | let pp pp_elt fmt set =
306 | let pp_comma fmt () = Format.fprintf fmt ",@ " in
307 | Format.fprintf fmt "{%a}"
308 | (Format.pp_print_list ~pp_sep:pp_comma pp_elt)
309 | (elements set)
310 |
--------------------------------------------------------------------------------
/core/Set_.mli:
--------------------------------------------------------------------------------
1 | (*pad: taken from set.ml from stdlib ocaml, functor sux: module Make(Ord: OrderedType) = *)
2 | (* with some addons such as from list *)
3 | (***********************************************************************)
4 | (* *)
5 | (* Objective Caml *)
6 | (* *)
7 | (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
8 | (* *)
9 | (* Copyright 1996 Institut National de Recherche en Informatique et *)
10 | (* en Automatique. All rights reserved. This file is distributed *)
11 | (* under the terms of the GNU Library General Public License, with *)
12 | (* the special exception on linking described in file ../LICENSE. *)
13 | (* *)
14 | (***********************************************************************)
15 |
16 | (* set.mli 1.32 2004/04/23 10:01:54 xleroy Exp $ *)
17 |
18 | (** Sets over ordered types.
19 |
20 | This module implements the set data structure, given a total ordering
21 | function over the set elements. All operations over sets
22 | are purely applicative (no side-effects).
23 | The implementation uses balanced binary trees, and is therefore
24 | reasonably efficient: insertion and membership take time
25 | logarithmic in the size of the set, for instance.
26 | *)
27 |
28 | (* pad:
29 | module type OrderedType =
30 | sig
31 | type t
32 | (** The type of the set elements. *)
33 | val compare : t -> t -> int
34 | (** A total ordering function over the set elements.
35 | This is a two-argument function [f] such that
36 | [f e1 e2] is zero if the elements [e1] and [e2] are equal,
37 | [f e1 e2] is strictly negative if [e1] is smaller than [e2],
38 | and [f e1 e2] is strictly positive if [e1] is greater than [e2].
39 | Example: a suitable ordering function is the generic structural
40 | comparison function {!Pervasives.compare}. *)
41 | end
42 | (** Input signature of the functor {!Set.Make}. *)
43 | *)
44 | (*
45 | module type S =
46 | sig
47 | *)
48 | (* type elt *)
49 | (** The type of the set elements. *)
50 |
51 | type 'elt t
52 | (** The type of sets. *)
53 |
54 | val empty : 'elt t
55 | (** The empty set. *)
56 |
57 | val is_empty : 'elt t -> bool
58 | (** Test whether a set is empty or not. *)
59 |
60 | val mem : 'elt -> 'elt t -> bool
61 | (** [mem x s] tests whether [x] belongs to the set [s]. *)
62 |
63 | val add : 'elt -> 'elt t -> 'elt t
64 | (** [add x s] returns a set containing all elements of [s],
65 | plus [x]. If [x] was already in [s], [s] is returned unchanged. *)
66 |
67 | val singleton : 'elt -> 'elt t
68 | (** [singleton x] returns the one-element set containing only [x]. *)
69 |
70 | val remove : 'elt -> 'elt t -> 'elt t
71 | (** [remove x s] returns a set containing all elements of [s],
72 | except [x]. If [x] was not in [s], [s] is returned unchanged. *)
73 |
74 | val union : 'elt t -> 'elt t -> 'elt t
75 | (** Set union. *)
76 |
77 | val inter : 'elt t -> 'elt t -> 'elt t
78 | (** Set intersection. *)
79 |
80 | val diff : 'elt t -> 'elt t -> 'elt t
81 | (** Set difference. *)
82 |
83 | val compare : 'elt t -> 'elt t -> int
84 | (** Total ordering between sets. Can be used as the ordering function
85 | for doing sets of sets. *)
86 |
87 | val equal : 'elt t -> 'elt t -> bool
88 | (** [equal s1 s2] tests whether the sets [s1] and [s2] are
89 | equal, that is, contain equal elements. *)
90 |
91 | val subset : 'elt t -> 'elt t -> bool
92 | (** [subset s1 s2] tests whether the set [s1] is a subset of
93 | the set [s2]. *)
94 |
95 | val iter : ('elt -> unit) -> 'elt t -> unit
96 | (** [iter f s] applies [f] in turn to all elements of [s].
97 | The elements of [s] are presented to [f] in increasing order
98 | with respect to the ordering over the type of the elements. *)
99 |
100 | val fold : ('elt -> 'a -> 'a) -> 'elt t -> 'a -> 'a
101 | (** [fold f s a] computes [(f xN ... (f x2 (f x1 a))...)],
102 | where [x1 ... xN] are the elements of [s], in increasing order. *)
103 |
104 | val for_all : ('elt -> bool) -> 'elt t -> bool
105 | (** [for_all p s] checks if all elements of the set
106 | satisfy the predicate [p]. *)
107 |
108 | val exists : ('elt -> bool) -> 'elt t -> bool
109 | (** [exists p s] checks if at least one element of
110 | the set satisfies the predicate [p]. *)
111 |
112 | val filter : ('elt -> bool) -> 'elt t -> 'elt t
113 | (** [filter p s] returns the set of all elements in [s]
114 | that satisfy predicate [p]. *)
115 |
116 | val partition : ('elt -> bool) -> 'elt t -> 'elt t * 'elt t
117 | (** [partition p s] returns a pair of sets [(s1, s2)], where
118 | [s1] is the set of all the elements of [s] that satisfy the
119 | predicate [p], and [s2] is the set of all the elements of
120 | [s] that do not satisfy [p]. *)
121 |
122 | val cardinal : 'elt t -> int
123 | (** Return the number of elements of a set. *)
124 |
125 | val elements : 'elt t -> 'elt list
126 | (** Return the list of all elements of the given set.
127 | The returned list is sorted in increasing order with respect
128 | to the ordering [Ord.compare], where [Ord] is the argument
129 | given to {!Set.Make}. *)
130 |
131 | val min_elt : 'elt t -> 'elt
132 | (** Return the smallest element of the given set
133 | (with respect to the [Ord.compare] ordering), or raise
134 | [Not_found] if the set is empty. *)
135 |
136 | val max_elt : 'elt t -> 'elt
137 | (** Same as {!Set.S.min_elt}, but returns the largest element of the
138 | given set. *)
139 |
140 | val choose : 'elt t -> 'elt
141 | (** Return one element of the given set, or raise [Not_found] if
142 | the set is empty. Which element is chosen is unspecified,
143 | but equal elements will be chosen for equal sets. *)
144 |
145 | val split : 'elt -> 'elt t -> 'elt t * bool * 'elt t
146 | (** [split x s] returns a triple [(l, present, r)], where
147 | [l] is the set of elements of [s] that are
148 | strictly less than [x];
149 | [r] is the set of elements of [s] that are
150 | strictly greater than [x];
151 | [present] is [false] if [s] contains no element equal to [x],
152 | or [true] if [s] contains an element equal to [x]. *)
153 |
154 | val of_list : 'elt list -> 'elt t
155 | (*
156 | end
157 | (** Output signature of the functor {!Set.Make}. *)
158 |
159 | module Make (Ord : OrderedType) : S with type elt = Ord.t
160 | (** Functor building an implementation of the set structure
161 | given a totally ordered type. *)
162 | *)
163 |
164 | (* Pretty-print a set as e.g. {1, 2, 3} *)
165 | val pp :
166 | (Format.formatter -> 'elt -> unit) -> Format.formatter -> 'elt t -> unit
167 |
--------------------------------------------------------------------------------
/core/Time.ml:
--------------------------------------------------------------------------------
1 | type posix = float
2 | let millis_to_posix n =
3 | float_of_int n
4 | let posix_to_millis n =
5 | int_of_float ( n *. 1000.)
6 |
--------------------------------------------------------------------------------
/core/dune:
--------------------------------------------------------------------------------
1 | (library
2 | (public_name elm_core)
3 | (wrapped false)
4 | (libraries
5 | ; commons; was for Set_.ml but now included here
6 | )
7 | ; (preprocess (pps profiling.ppx))
8 | )
9 |
--------------------------------------------------------------------------------
/docs/elm_core/Basics/index.html:
--------------------------------------------------------------------------------
1 |
2 |
Basics (elm_core.Basics) Up – elm_core » Basicsval (/..) : int -> int -> int
val (+..) : int -> int -> int
val (-..) : int -> int -> int
val (*..) : int -> int -> int
val (/) : float -> float -> float
val (+) : float -> float -> float
val (-) : float -> float -> float
val (*) : float -> float -> float
val mod_by : int -> int -> int
val degrees_to_radians : float -> float
val radians_to_degrees : float -> float
val turns : float -> float
val clamp : 'number -> 'number -> 'number -> 'number
3 |
--------------------------------------------------------------------------------
/docs/elm_core/Color/index.html:
--------------------------------------------------------------------------------
1 |
2 | Color (elm_core.Color) Up – elm_core » Colortype t =
| Hex of string
| Rgb of int * int * int
val color_clamp : int -> int
val rgb : int -> int -> int -> t
3 |
--------------------------------------------------------------------------------
/docs/elm_core/Keyboard/index.html:
--------------------------------------------------------------------------------
1 |
2 | Keyboard (elm_core.Keyboard) Up – elm_core » Keyboard
3 |
--------------------------------------------------------------------------------
/docs/elm_core/Set/index.html:
--------------------------------------------------------------------------------
1 |
2 | Set (elm_core.Set) Up – elm_core » Set
3 |
--------------------------------------------------------------------------------
/docs/elm_core/Time/index.html:
--------------------------------------------------------------------------------
1 |
2 | Time (elm_core.Time) Up – elm_core » Timeval millis_to_posix : int -> float
val posix_to_millis : float -> int
3 |
--------------------------------------------------------------------------------
/docs/elm_core/index.html:
--------------------------------------------------------------------------------
1 |
2 | index (elm_core.index) Up – elm_core Library elm_coreThis library exposes the following toplevel modules:
3 |
--------------------------------------------------------------------------------
/docs/elm_playground/Playground_platform/index.html:
--------------------------------------------------------------------------------
1 |
2 | Playground_platform (elm_playground.Playground_platform) Up – elm_playground » Playground_platformModule Playground_platform
3 |
--------------------------------------------------------------------------------
/docs/elm_playground/index.html:
--------------------------------------------------------------------------------
1 |
2 | index (elm_playground.index) Up – elm_playground OCaml Elm PlaygroundCreate pictures, animations, and games with OCaml!
This is a port of the excellent Elm playground package to OCaml.
This is the package I wanted when I was learning programming. Start by putting shapes on screen and work up to making games. I hope this package will be fun for a broad range of ages and backgrounds!
PicturesA picture is a list of shapes. For example, this picture combines a brown rectangle and a green circle to make a tree
open Playground
3 |
4 | let app =
5 | picture [
6 | rectangle brown 40. 200.;
7 | circle green 100.
8 | |> move_up 100.;
9 | ]
10 |
11 | let main = Playground_platform.run_app app
Play around to get familiar with all the different shapes and transformations in the library.
AnimationsAn animation is a list of shapes that changes over time. For example, here is a spinning triangle:
open Playground
12 |
13 | let view time = [
14 | triangle orange 50.
15 | |> rotate (spin 8. time);
16 | ]
17 |
18 | let app = animation view
19 |
20 | let main = Playground_platform.run_app app
It will do a full spin every 8 seconds.
Maybe try making a car with spinning octogons as wheels? Try using Playground.wave
to move things back-and-forth? Try using Playground.zigzag
to fade things in-and-out?
GamesA game lets you use input from the mouse and keyboard to change your picture. For example, here is a square that moves around based on the arrow keys:
open Playground
21 |
22 | let view _computer (x, y) = [
23 | square blue 40.
24 | |> move x y
25 | ]
26 |
27 | let update computer (x, y) =
28 | (x +. to_x computer.keyboard, y +. to_y computer.keyboard)
29 |
30 | let app =
31 | game view update (0., 0.)
32 |
33 | let main = Playground_platform.run_app app
Every game has three important parts:
memory
- Store information. Our example stores (x,y)
coordinates.update
- Update the memory based on mouse movements, key presses, etc. Our example moves the (x,y)
coordinate around based on the arrow keys.view
- Turn the memory into a picture. Our example just shows one blue square at the (x,y)
coordinate we have been tracking in memory.When you start making fancier games, you will store fancier things in memory. There is a lot of room to develop your programming skills here: Making lists, using records, creating custom types, etc.
I started off trying to make Pong, then worked on games like Breakout and Space Invaders as I learned more and more. It was really fun, and I hope it will be for you as well!
Index of modules
34 |
--------------------------------------------------------------------------------
/docs/elm_playground_native/index.html:
--------------------------------------------------------------------------------
1 |
2 | index (elm_playground_native.index) Up – elm_playground_native elm_playground_native index
3 |
--------------------------------------------------------------------------------
/docs/elm_playground_web/index.html:
--------------------------------------------------------------------------------
1 |
2 | index (elm_playground_web.index) Up – elm_playground_web
3 |
--------------------------------------------------------------------------------
/docs/elm_system/Cmd/index.html:
--------------------------------------------------------------------------------
1 |
2 | Cmd (elm_system.Cmd) Up – elm_system » Cmdtype 'msg t =
| None
| Msg of 'msg
3 |
--------------------------------------------------------------------------------
/docs/elm_system/Sub/index.html:
--------------------------------------------------------------------------------
1 |
2 | Sub (elm_system.Sub) Up – elm_system » Subtype 'msg onesub =
| SubTick of Time.posix -> 'msg
| SubMouseMove of (float * float) -> 'msg
| SubMouseDown of unit -> 'msg
| SubMouseUp of unit -> 'msg
| SubKeyDown of Keyboard.key -> 'msg
| SubKeyUp of Keyboard.key -> 'msg
type 'msg t = 'msg onesub list
val batch : 'a list list -> 'a list
val on_animation_frame : (Time.posix -> 'msg ) -> 'msg t
val on_mouse_move : ((float * float) -> 'msg ) -> 'msg t
val on_mouse_down : (unit -> 'msg ) -> 'msg t
val on_mouse_up : (unit -> 'msg ) -> 'msg t
type event =
| ETick of float
| EMouseMove of int * int
| EMouseButton of bool
| EKeyChanged of bool * Keyboard.key
val find_map_opt : ('a -> 'b option ) -> 'a list -> 'b option
val event_to_msgopt : event -> 'a onesub list -> 'a option
3 |
--------------------------------------------------------------------------------
/docs/elm_system/index.html:
--------------------------------------------------------------------------------
1 |
2 | index (elm_system.index) Up – elm_system Library elm_systemThis library exposes the following toplevel modules:
3 |
--------------------------------------------------------------------------------
/docs/examples/Animation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Keyboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Mario.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Mouse.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Picture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Smiley.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Turtle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/Words.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | index
5 |
6 |
7 |
8 |
9 |
10 |
11 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/games/Asteroid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/games/Pong.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/games/Snake.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/games/Template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/games/Tetris.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/games/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | index
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Games
13 |
14 | Snake: src app
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | index
5 |
6 |
7 |
8 |
9 |
10 |
11 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_AMS-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_AMS-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Caligraphic-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Caligraphic-Bold.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Caligraphic-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Caligraphic-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Fraktur-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Fraktur-Bold.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Fraktur-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Fraktur-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Main-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Main-Bold.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Main-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Main-BoldItalic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Main-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Main-Italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Main-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Main-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Math-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Math-BoldItalic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Math-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Math-Italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_SansSerif-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_SansSerif-Bold.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_SansSerif-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_SansSerif-Italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_SansSerif-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_SansSerif-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Script-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Script-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Size1-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Size1-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Size2-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Size2-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Size3-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Size3-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Size4-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Size4-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/KaTeX_Typewriter-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/KaTeX_Typewriter-Regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-mono-v14-latin-500.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-mono-v14-latin-500.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-mono-v14-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-mono-v14-latin-regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-500.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-500.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-500italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-500italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-700.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-700italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-700italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/fira-sans-v17-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/fira-sans-v17-latin-regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/noticia-text-v15-latin-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/noticia-text-v15-latin-700.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/noticia-text-v15-latin-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/noticia-text-v15-latin-italic.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/fonts/noticia-text-v15-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/odoc.support/fonts/noticia-text-v15-latin-regular.woff2
--------------------------------------------------------------------------------
/docs/odoc.support/odoc_search.js:
--------------------------------------------------------------------------------
1 | /* The browsers interpretation of the CORS origin policy prevents to run
2 | webworkers from javascript files fetched from the file:// protocol. This hack
3 | is to workaround this restriction. */
4 | function createWebWorker() {
5 | var searchs = search_urls.map((search_url) => {
6 | let parts = document.location.href.split("/");
7 | parts[parts.length - 1] = search_url;
8 | return '"' + parts.join("/") + '"';
9 | });
10 | blobContents = ["importScripts(" + searchs.join(",") + ");"];
11 | var blob = new Blob(blobContents, { type: "application/javascript" });
12 | var blobUrl = URL.createObjectURL(blob);
13 |
14 | var worker = new Worker(blobUrl);
15 | URL.revokeObjectURL(blobUrl);
16 |
17 | return worker;
18 | }
19 |
20 | var worker;
21 | var waiting = 0;
22 |
23 | function wait() {
24 | waiting = waiting + 1;
25 | document.querySelector(".search-snake").classList.add("search-busy");
26 | }
27 |
28 | function stop_waiting() {
29 | if (waiting > 0) waiting = waiting - 1;
30 | else waiting = 0;
31 | if (waiting == 0) {
32 | document.querySelector(".search-snake").classList.remove("search-busy");
33 | }
34 | }
35 |
36 | document.querySelector(".search-bar").addEventListener("focus", (ev) => {
37 | if (typeof worker == "undefined") {
38 | worker = createWebWorker();
39 | worker.onmessage = (e) => {
40 | stop_waiting();
41 | let results = e.data;
42 | let search_results = document.querySelector(".search-result");
43 | search_results.innerHTML = "";
44 | let f = (entry) => {
45 | let search_result = document.createElement("a");
46 | search_result.classList.add("search-entry");
47 | search_result.href = base_url + entry.url;
48 | search_result.innerHTML = entry.html;
49 | search_results.appendChild(search_result);
50 | };
51 | results.forEach(f);
52 | let search_request = document.querySelector(".search-bar").value;
53 | if (results.length == 0 && search_request != "") {
54 | let no_result = document.createElement("div");
55 | no_result.classList.add("search-no-result");
56 | no_result.innerText = "No result...";
57 | search_results.appendChild(no_result);
58 | }
59 | };
60 | }
61 | });
62 |
63 | document.querySelector(".search-bar").addEventListener("input", (ev) => {
64 | wait();
65 | worker.postMessage(ev.target.value);
66 | });
67 |
--------------------------------------------------------------------------------
/docs/screenshots/example-animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-animation.png
--------------------------------------------------------------------------------
/docs/screenshots/example-keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-keyboard.png
--------------------------------------------------------------------------------
/docs/screenshots/example-misc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-misc.png
--------------------------------------------------------------------------------
/docs/screenshots/example-mouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-mouse.png
--------------------------------------------------------------------------------
/docs/screenshots/example-picture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-picture.png
--------------------------------------------------------------------------------
/docs/screenshots/example-smiley.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-smiley.png
--------------------------------------------------------------------------------
/docs/screenshots/example-turtle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-turtle.png
--------------------------------------------------------------------------------
/docs/screenshots/example-words.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/example-words.png
--------------------------------------------------------------------------------
/docs/screenshots/game-asteroid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/game-asteroid.png
--------------------------------------------------------------------------------
/docs/screenshots/game-pong.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/game-pong.png
--------------------------------------------------------------------------------
/docs/screenshots/game-snake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/game-snake.png
--------------------------------------------------------------------------------
/docs/screenshots/game-tetris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/game-tetris.png
--------------------------------------------------------------------------------
/docs/screenshots/keyboard-game-native.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/keyboard-game-native.png
--------------------------------------------------------------------------------
/docs/screenshots/keyboard-game-start-native.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/screenshots/keyboard-game-start-native.png
--------------------------------------------------------------------------------
/docs/toy-native-example/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | dune build --root .
3 | clean:
4 | dune clean --root .
5 |
--------------------------------------------------------------------------------
/docs/toy-native-example/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/toy-native-example/README.md
--------------------------------------------------------------------------------
/docs/toy-native-example/Toy.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | (* the (x, y) position of the blue square *)
4 | type model = (float * float)
5 |
6 | let initial_state : model = (0., 0.)
7 |
8 | let view _computer (x, y) = [
9 | square blue 40.
10 | |> move x y
11 | ]
12 |
13 | let update computer (x, y) =
14 | (x +. to_x computer.keyboard, y +. to_y computer.keyboard)
15 |
16 | let app =
17 | game view update initial_state
18 |
19 | let _main = Playground_platform.run_app app
20 |
--------------------------------------------------------------------------------
/docs/toy-native-example/dune:
--------------------------------------------------------------------------------
1 | (executable
2 | (name Toy)
3 | (libraries
4 | elm_playground
5 | elm_playground_native
6 | ))
7 |
--------------------------------------------------------------------------------
/docs/toy-native-example/dune-project:
--------------------------------------------------------------------------------
1 | (lang dune 3.0)
2 |
--------------------------------------------------------------------------------
/docs/toy-native-example/toy-game.opam:
--------------------------------------------------------------------------------
1 | opam-version: "2.0"
2 |
3 | depends: [
4 | "ocaml" {>= "4.14.0"}
5 | "dune" {>= "2.0.0"}
6 | "elm_playground"
7 | "elm_playground_native"
8 | ]
9 |
10 | synopsis: "One of the OCaml Elm Playground examples"
11 | homepage: "https://github.com/aryx/ocaml-elm-playground"
12 | bug-reports: "https://github.com/aryx/ocaml-elm-playground/issues"
13 | author: "Yoann Padioleau "
14 | license: "MIT"
15 | maintainer: "Yoann Padioleau "
16 |
--------------------------------------------------------------------------------
/docs/toy-web-example/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | dune build --root .
3 | cp _build/default/Toy.bc.js static
4 | clean:
5 | dune clean --root .
6 |
7 | release:
8 | dune build --root . --profile=release-js
9 | cp _build/default/Toy.bc.js static
10 |
--------------------------------------------------------------------------------
/docs/toy-web-example/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aryx/ocaml-elm-playground/1049f8ae6e73a418dcfc72c36e7417a1ebf7d67f/docs/toy-web-example/README.md
--------------------------------------------------------------------------------
/docs/toy-web-example/Toy.ml:
--------------------------------------------------------------------------------
1 | ../toy-native-example/Toy.ml
--------------------------------------------------------------------------------
/docs/toy-web-example/dune:
--------------------------------------------------------------------------------
1 | (executable
2 | (name Toy)
3 | (libraries
4 | elm_playground
5 | elm_playground_web
6 | )
7 | (modes js)
8 | (link_flags -no-check-prims)
9 | )
10 |
11 | (env
12 | (dev
13 | ; (flags (:standard -w -6-32-37-69))
14 | ; with dev jsoo Toy.bc.js is =~3MB
15 | (js_of_ocaml
16 | (flags
17 | (
18 | :standard ; pretty and sourcemap
19 | --no-inline
20 | --debug-info
21 | --disable staticeval
22 | --disable share
23 | ; js_of_ocaml has a bug w/ shortvars after 5.5.2 so let's disable it
24 | --disable shortvar
25 | ; gives us better error messages
26 | --enable with-js-error))))
27 | (release
28 | (flags (:standard)))
29 | ; with release-js Toy.bc.js is =~120KB
30 | (release-js
31 | (js_of_ocaml
32 | (flags
33 | (
34 | --disable shortvar
35 | --enable with-js-error))))
36 | )
37 |
--------------------------------------------------------------------------------
/docs/toy-web-example/dune-project:
--------------------------------------------------------------------------------
1 | (lang dune 3.0)
2 |
--------------------------------------------------------------------------------
/docs/toy-web-example/static/Toy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/toy-web-example/toy-web-game.opam:
--------------------------------------------------------------------------------
1 | opam-version: "2.0"
2 |
3 | depends: [
4 | "ocaml" {>= "4.14.0"}
5 | "dune" {>= "2.0.0"}
6 | "elm_playground"
7 | "elm_playground_web"
8 | ]
9 |
10 | synopsis: "One of the OCaml Elm Playground examples"
11 | homepage: "https://github.com/aryx/ocaml-elm-playground"
12 | bug-reports: "https://github.com/aryx/ocaml-elm-playground/issues"
13 | author: "Yoann Padioleau "
14 | license: "MIT"
15 | maintainer: "Yoann Padioleau "
16 |
--------------------------------------------------------------------------------
/dune:
--------------------------------------------------------------------------------
1 | (env
2 | (dev
3 | ;TODO FIX! remove all those -xxx
4 | (flags (:standard -w -6-32-37-69))
5 | ; with dev jsoo Animation.bc.js is =~3MB
6 | (js_of_ocaml
7 | ; copy from semgrep-pro/dune file
8 | (flags
9 | (
10 | :standard ; pretty and sourcemap
11 | --no-inline
12 | --debug-info
13 | --disable staticeval
14 | --disable share
15 | ; js_of_ocaml has a bug w/ shortvars after 5.5.2 so let's disable it
16 | --disable shortvar
17 | ; gives us better error messages
18 | --enable with-js-error))))
19 | (release
20 | (flags (:standard)))
21 | ; with release-js Animation.bc.js is =~120KB
22 | ; if you give a fake profile like dune build example_js --profile=foo
23 | ; you can get even lower with 90KB but maybe the options below are useful
24 | ; TODO: use --opt? in semgrep-pro we don't because it says it can break things
25 | (release-js
26 | (js_of_ocaml
27 | (flags
28 | (
29 | --disable shortvar
30 | --enable with-js-error))))
31 |
32 | )
33 |
--------------------------------------------------------------------------------
/dune-project:
--------------------------------------------------------------------------------
1 | (lang dune 3.0)
2 |
3 | (name elm_playground)
4 |
5 | (generate_opam_files true)
6 |
7 | (license "LGPL-2.1-only")
8 |
9 | (authors
10 | "Yoann Padioleau = "4.07.0"))
39 | )
40 | )
41 |
42 | (package (name elm_system)
43 | (synopsis "Imitate system Elm functions for ease of porting")
44 | (description "")
45 | (depends
46 | elm_core
47 | )
48 | )
49 |
50 | (package (name elm_playground)
51 | (synopsis "Interface of the Playground")
52 | (description "\
53 | This is just the interface of the playground. You'll need an
54 | actual platform library to compile your application
55 | (elm_playground_native or elm_playground_web)
56 | ")
57 | (depends
58 | elm_core
59 | elm_system
60 | )
61 | )
62 | (package (name elm_playground_native)
63 | (synopsis "Native TDSL-based playground platform")
64 | (description "")
65 | (depends
66 | ; tsdl fails in 5.2.0 because of some missing unix
67 | ; the lib fails in 4.07 because of some syntax error about an operator
68 | (ocaml (and (< "5.2.0") (>= "4.08.0")))
69 | logs
70 | (cairo2 (>= "0.6.4"))
71 | tsdl
72 | imagelib
73 | ocurl
74 | elm_playground
75 | )
76 | )
77 | (package (name elm_playground_web)
78 | (synopsis "Web vdom-based playground platform")
79 | (description "")
80 | (depends
81 | ; gen_js_api fails to build in 4.10.0
82 | (ocaml (>= "4.11.0"))
83 | vdom
84 | elm_playground
85 | )
86 | )
87 |
--------------------------------------------------------------------------------
/elm_core.opam:
--------------------------------------------------------------------------------
1 | # This file is generated by dune, edit dune-project instead
2 | opam-version: "2.0"
3 | version: "0.2.0"
4 | synopsis: "Imitate core Elm functions for ease of porting"
5 | description: """
6 | Nope
7 | """
8 | maintainer: ["Yoann Padioleau = "3.0"}
15 | "ocaml" {>= "4.07.0"}
16 | "odoc" {with-doc}
17 | ]
18 | build: [
19 | ["dune" "subst"] {dev}
20 | [
21 | "dune"
22 | "build"
23 | "-p"
24 | name
25 | "-j"
26 | jobs
27 | "@install"
28 | "@runtest" {with-test}
29 | "@doc" {with-doc}
30 | ]
31 | ]
32 | dev-repo: "git+https://github.com/aryx/ocaml-elm-playground.git"
33 |
--------------------------------------------------------------------------------
/elm_playground.opam:
--------------------------------------------------------------------------------
1 | # This file is generated by dune, edit dune-project instead
2 | opam-version: "2.0"
3 | version: "0.2.0"
4 | synopsis: "Interface of the Playground"
5 | description: """
6 | This is just the interface of the playground. You'll need an
7 | actual platform library to compile your application
8 | (elm_playground_native or elm_playground_web)
9 | """
10 | maintainer: ["Yoann Padioleau = "3.0"}
17 | "elm_core"
18 | "elm_system"
19 | "odoc" {with-doc}
20 | ]
21 | build: [
22 | ["dune" "subst"] {dev}
23 | [
24 | "dune"
25 | "build"
26 | "-p"
27 | name
28 | "-j"
29 | jobs
30 | "@install"
31 | "@runtest" {with-test}
32 | "@doc" {with-doc}
33 | ]
34 | ]
35 | dev-repo: "git+https://github.com/aryx/ocaml-elm-playground.git"
36 |
--------------------------------------------------------------------------------
/elm_playground_native.opam:
--------------------------------------------------------------------------------
1 | # This file is generated by dune, edit dune-project instead
2 | opam-version: "2.0"
3 | version: "0.2.0"
4 | synopsis: "Native TDSL-based playground platform"
5 | description: ""
6 | maintainer: ["Yoann Padioleau = "3.0"}
13 | "ocaml" {< "5.2.0" & >= "4.08.0"}
14 | "logs"
15 | "cairo2" {>= "0.6.4"}
16 | "tsdl"
17 | "imagelib"
18 | "ocurl"
19 | "elm_playground"
20 | "odoc" {with-doc}
21 | ]
22 | build: [
23 | ["dune" "subst"] {dev}
24 | [
25 | "dune"
26 | "build"
27 | "-p"
28 | name
29 | "-j"
30 | jobs
31 | "@install"
32 | "@runtest" {with-test}
33 | "@doc" {with-doc}
34 | ]
35 | ]
36 | dev-repo: "git+https://github.com/aryx/ocaml-elm-playground.git"
37 | available: [ os-family != "windows" ]
38 |
--------------------------------------------------------------------------------
/elm_playground_native.opam.template:
--------------------------------------------------------------------------------
1 | available: [ os-family != "windows" ]
2 |
--------------------------------------------------------------------------------
/elm_playground_web.opam:
--------------------------------------------------------------------------------
1 | # This file is generated by dune, edit dune-project instead
2 | opam-version: "2.0"
3 | version: "0.2.0"
4 | synopsis: "Web vdom-based playground platform"
5 | description: ""
6 | maintainer: ["Yoann Padioleau = "3.0"}
13 | "ocaml" {>= "4.11.0"}
14 | "vdom"
15 | "elm_playground"
16 | "odoc" {with-doc}
17 | ]
18 | build: [
19 | ["dune" "subst"] {dev}
20 | [
21 | "dune"
22 | "build"
23 | "-p"
24 | name
25 | "-j"
26 | jobs
27 | "@install"
28 | "@runtest" {with-test}
29 | "@doc" {with-doc}
30 | ]
31 | ]
32 | dev-repo: "git+https://github.com/aryx/ocaml-elm-playground.git"
33 |
--------------------------------------------------------------------------------
/elm_system.opam:
--------------------------------------------------------------------------------
1 | # This file is generated by dune, edit dune-project instead
2 | opam-version: "2.0"
3 | version: "0.2.0"
4 | synopsis: "Imitate system Elm functions for ease of porting"
5 | description: ""
6 | maintainer: ["Yoann Padioleau = "3.0"}
13 | "elm_core"
14 | "odoc" {with-doc}
15 | ]
16 | build: [
17 | ["dune" "subst"] {dev}
18 | [
19 | "dune"
20 | "build"
21 | "-p"
22 | name
23 | "-j"
24 | jobs
25 | "@install"
26 | "@runtest" {with-test}
27 | "@doc" {with-doc}
28 | ]
29 | ]
30 | dev-repo: "git+https://github.com/aryx/ocaml-elm-playground.git"
31 |
--------------------------------------------------------------------------------
/examples/Animation.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/animation *)
2 | open Playground
3 |
4 | let view time = [
5 | octagon darkGray 36.
6 | |> move_left 100.
7 | |> rotate (spin 3. time);
8 | octagon darkGray 36.
9 | |> move_right 100.
10 | |> rotate (spin 3. time);
11 | rectangle red 300. 80.
12 | |> move_up (wave 50. 54. 2. time)
13 | |> rotate (zigzag (-. 2.) 2. 8. time);
14 | ]
15 |
16 | let app =
17 | animation view
18 |
19 | let main = Playground_platform.run_app app
20 |
--------------------------------------------------------------------------------
/examples/Keyboard.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/mouse *)
2 | open Playground
3 |
4 | let view _computer (x, y) = [
5 | square blue 40.
6 | |> move x y
7 | ]
8 |
9 | let update computer (x, y) =
10 | (x +. to_x computer.keyboard, y +. to_y computer.keyboard)
11 |
12 | let app =
13 | game view update (0., 0.)
14 |
15 | let main = Playground_platform.run_app app
16 |
--------------------------------------------------------------------------------
/examples/Mario.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/mario *)
2 | open Playground
3 | open Basics (* float arithmetics *)
4 |
5 | type model = {
6 | x: number;
7 | y: number;
8 | vx: number;
9 | vy: number;
10 | dir: string
11 | }
12 |
13 | let initial_model =
14 | { x = 0.;
15 | y = 0.;
16 | vx = 0.;
17 | vy = 0.;
18 | dir = "right"
19 | }
20 |
21 | let to_gif mario =
22 | if mario.y > 0. then
23 | "https://elm-lang.org/images/mario/jump/" ^ mario.dir ^ ".gif"
24 | else if mario.vx <> 0. then
25 | "https://elm-lang.org/images/mario/walk/" ^ mario.dir ^ ".gif"
26 | else
27 | "https://elm-lang.org/images/mario/stand/" ^ mario.dir ^ ".gif"
28 |
29 | let view computer mario =
30 | let w = computer.screen.width in
31 | let h = computer.screen.height in
32 | let b = computer.screen.bottom in
33 |
34 | [ rectangle (rgb 174 238 238) w h;
35 | rectangle (rgb 74 163 41) w 100.
36 | |> move_y b;
37 | image 70. 70. (to_gif mario)
38 |
39 | |> move mario.x (b + 76. + mario.y)
40 | ]
41 |
42 |
43 | let update computer mario =
44 | let dt = 1.666 in
45 | let vx = to_x computer.keyboard in
46 | let vy =
47 | if mario.y = 0. then
48 | if computer.keyboard.kup then 5. else 0.
49 | else
50 | mario.vy - dt / 8.
51 | in
52 | let x = mario.x + dt * vx in
53 | let y = mario.y + dt * vy in
54 | { x;
55 | y = max 0. y;
56 | vx;
57 | vy;
58 | dir = if vx = 0. then mario.dir else if vx < 0. then "left" else "right"
59 | }
60 |
61 | let app = game view update initial_model
62 |
63 | let main = Playground_platform.run_app app
64 |
65 |
--------------------------------------------------------------------------------
/examples/Misc.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | let star =
4 | group
5 | [ triangle yellow 20.;
6 | triangle yellow 20.
7 | |> rotate 180.;
8 | ]
9 |
10 | let app =
11 | picture [
12 | polygon black [ (-10., -20.); (0., 100.); (10., -20.)];
13 | rectangle green 10. 10. |> scale 2. |> move 20. 20. |> rotate 45.;
14 |
15 | star |> move 100. 100. |> rotate 5.;
16 | star |> move (-120.) 40. |> rotate 20.;
17 | star |> move 80. (-150.) |> rotate 32.;
18 | star |> move (-90.) (-30.) |> rotate (-16.);
19 | ]
20 |
21 | let main = Playground_platform.run_app app
22 |
--------------------------------------------------------------------------------
/examples/Mouse.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/mouse *)
2 | open Playground
3 |
4 | let view computer _memory = [
5 | rectangle yellow computer.screen.width computer.screen.height;
6 | circle lightPurple 30.
7 | |> move_x computer.mouse.mx
8 | |> move_y computer.mouse.my
9 | |> fade (if computer.mouse.mdown then 0.2 else 1.)
10 | ]
11 |
12 | let update _computer () =
13 | ()
14 |
15 | let app =
16 | game view update ()
17 |
18 | let main = Playground_platform.run_app app
19 |
--------------------------------------------------------------------------------
/examples/Picture.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/picture *)
2 | open Playground
3 |
4 | let app =
5 | picture [
6 | rectangle brown 40. 200.
7 | |> move_down 80.;
8 | circle green 100.
9 | |> move_up 100.;
10 | ]
11 |
12 | let main = Playground_platform.run_app app
13 |
--------------------------------------------------------------------------------
/examples/Smiley.ml:
--------------------------------------------------------------------------------
1 | (* from elm-playground/examples.smiley.elm *)
2 | open Playground
3 |
4 | let app =
5 | picture [
6 | circle lightYellow 200.;
7 |
8 | (* left eye *)
9 | circle white 50.
10 | |> move_left 70.
11 | |> move_up 50.;
12 | circle black 10.
13 | |> move_left 75.
14 | |> move_up 40.;
15 |
16 | (* right eye *)
17 | circle white 50.
18 | |> move_right 70.
19 | |> move_up 50.;
20 | circle black 10.
21 | |> move_right 65.
22 | |> move_up 40.;
23 |
24 | (* mouth *)
25 | oval black 180. 40.
26 | |> move_down 100.;
27 | oval lightYellow 180. 40.
28 | |> move_down 90.;
29 |
30 | ]
31 |
32 | let main = Playground_platform.run_app app
33 |
--------------------------------------------------------------------------------
/examples/Turtle.ml:
--------------------------------------------------------------------------------
1 | (* from https://elm-lang.org/examples/turtle *)
2 | open Playground
3 | open Basics
4 |
5 | type turtle = {
6 | x: number;
7 | y: number;
8 | angle: number;
9 | }
10 |
11 | let initial_turtle = { x = 0.; y = 0.; angle = 0. }
12 |
13 | let view computer turtle =
14 | [ rectangle blue computer.screen.width computer.screen.height;
15 | rectangle yellow 100. 100.;
16 | image 96. 96. "https://elm-lang.org/images/turtle.gif"
17 | (*"/tmp/turtle.gif" *)
18 | |> move turtle.x turtle.y
19 | |> rotate turtle.angle
20 | |> scale 2.
21 | ]
22 |
23 | let update computer turtle =
24 | { x = turtle.x +
25 | to_y computer.keyboard * cos (degrees_to_radians turtle.angle);
26 | y = turtle.y +
27 | to_y computer.keyboard * sin (degrees_to_radians turtle.angle);
28 | angle = turtle.angle - to_x computer.keyboard
29 | }
30 |
31 | let app = game view update initial_turtle
32 |
33 | let main = Playground_platform.run_app app
34 |
--------------------------------------------------------------------------------
/examples/Words.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | let app =
4 | picture [
5 | rectangle green 10. 10. |> scale 2. |> move 20. 20. |> rotate 45.;
6 | rectangle black 2. 2.;
7 | words green "foobar" |> scale 2. |> rotate 90.;
8 | ]
9 |
10 | let main = Playground_platform.run_app app
11 |
--------------------------------------------------------------------------------
/examples/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names
3 | Picture Smiley Words Misc
4 | Animation
5 | Mouse Keyboard Turtle Mario
6 | )
7 | (libraries
8 | elm_playground
9 | elm_playground_native ; implem of virtual elm_playground
10 | )
11 | )
12 |
--------------------------------------------------------------------------------
/examples_js/Animation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Animation.ml:
--------------------------------------------------------------------------------
1 | ../examples/Animation.ml
--------------------------------------------------------------------------------
/examples_js/Keyboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Keyboard.ml:
--------------------------------------------------------------------------------
1 | ../examples/Keyboard.ml
--------------------------------------------------------------------------------
/examples_js/Mario.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Mario.ml:
--------------------------------------------------------------------------------
1 | ../examples/Mario.ml
--------------------------------------------------------------------------------
/examples_js/Mouse.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Mouse.ml:
--------------------------------------------------------------------------------
1 | ../examples/Mouse.ml
--------------------------------------------------------------------------------
/examples_js/Picture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Picture.ml:
--------------------------------------------------------------------------------
1 | ../examples/Picture.ml
--------------------------------------------------------------------------------
/examples_js/Smiley.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Smiley.ml:
--------------------------------------------------------------------------------
1 | ../examples/Smiley.ml
--------------------------------------------------------------------------------
/examples_js/Template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Turtle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Turtle.ml:
--------------------------------------------------------------------------------
1 | ../examples/Turtle.ml
--------------------------------------------------------------------------------
/examples_js/Words.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples_js/Words.ml:
--------------------------------------------------------------------------------
1 | ../examples/Words.ml
--------------------------------------------------------------------------------
/examples_js/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names
3 | Smiley Picture Words
4 | Animation
5 | Mouse Keyboard
6 | Turtle Mario
7 | )
8 | (libraries
9 | elm_playground
10 | elm_playground_web ; implem of virtual elm_playground
11 | )
12 | (modes js)
13 | (link_flags -no-check-prims)
14 | )
15 |
--------------------------------------------------------------------------------
/examples_js/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games/Asteroid.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | (*****************************************************************************)
4 | (* Prelude *)
5 | (*****************************************************************************)
6 | (* Port of the Asteroid clone described at
7 | * http://www.informatik.uni-bremen.de/~clueth/haskell-in-space/
8 | * but using OCaml instead of Haskell, and using Playground instead of the
9 | * Hugs Graphics Library (HGL).
10 | *
11 | * See https://en.wikipedia.org/wiki/Asteroids_(video_game) for more
12 | * information on Asteroids, or its ancestor
13 | * Spacewar https://en.wikipedia.org/wiki/Spacewar!
14 | *
15 | *
16 | * TODO:
17 | * - see Elm clones of asteroids:
18 | *)
19 |
20 | (*****************************************************************************)
21 | (* Geometry *)
22 | (*****************************************************************************)
23 |
24 | (* todo? put that in a separate Geometry.ml library? with better types
25 | * less: advanced geometry types:
26 | * https://www.cs.cornell.edu/~asampson/media/papers/gator-oopsla2020-preprint.pdf
27 | *)
28 |
29 | (* orig: pad: would be simpler to use float everywhere? *)
30 | type point = {x: int; y: int }
31 |
32 | (* a vector is represented as an arrow from the origin (0, 0) to point *)
33 | type vector = point
34 |
35 | (*
36 | let (point_rotate: number -> point -> point) = fun w p ->
37 | let x' = float p.x in
38 | let y' = float p.y in
39 | { x = Basics.round (x' *. cos w +. y' *. sin w);
40 | y = Basics.round (-. x' *. sin w +. y' *. cos w);
41 | }
42 | *)
43 |
44 | let (vector: point -> point -> vector) = fun p1 p2 ->
45 | { x = p2.x - p1.x; y = p2.y - p1.y }
46 |
47 | let (vector_length: vector -> number) = fun v ->
48 | sqrt (float v.x ** 2. +. float v.y ** 2.)
49 |
50 | let (vector_add: vector -> vector -> vector) = fun v1 v2 ->
51 | {x = v1.x + v2.x; y = v1.y + v2.y }
52 |
53 | (* orig: was calling point_rotate but simpler to do directly *)
54 | let (polar: number -> number -> vector) = fun r phi ->
55 | { x = Basics.round (r *. cos phi); y = Basics.round (r *. sin phi) }
56 |
57 |
58 | (* orig: we can reuse Playground.shape *)
59 | type figure = Playground.shape
60 |
61 | (* Final "resolved" coordinates of a figure after translation/rotate.
62 | * Used for collision detection.
63 | *)
64 | type resolved_shape =
65 | (* TODO | Poly of point list *)
66 | | Circle of point * number (* radius *)
67 |
68 | let (intersect: resolved_shape -> resolved_shape -> bool) = fun x1 x2 ->
69 | match x1, x2 with
70 | | Circle (c1, r1), Circle (c2, r2) ->
71 | let v = vector c1 c2 in
72 | vector_length v <= r1 +. r2
73 |
74 | let (contains: resolved_shape -> point -> bool) = fun x p ->
75 | match x with
76 | | Circle (c, r) ->
77 | let v = vector c p in
78 | vector_length v <= r
79 |
80 | (*****************************************************************************)
81 | (* Model *)
82 | (*****************************************************************************)
83 |
84 | type 'a obj = {
85 | (* current state *)
86 | pos: point;
87 | velocity: vector;
88 |
89 | (* todo: resolved_shape at some point
90 | * orig: was shape, but simpler to store the figure and compute
91 | * the resolved shape when we need to.
92 | *)
93 | figure: figure;
94 |
95 | (* only used for the ship *)
96 | orientation: number;
97 |
98 | xtra: 'a;
99 | }
100 |
101 |
102 | (* simpler to make mutable *)
103 | type ship = {
104 | mutable thrust: number;
105 | mutable h_acceleration: number;
106 | }
107 | (* accelereration delta *)
108 | let a_delta = 1.
109 | (* turn delta *)
110 | let h_delta = 0.3
111 | (* Max velocity *)
112 | let v_max = 20.
113 |
114 | (* when drawn horizontally at 0 degrees *)
115 | let space_ship c =
116 | polygon c [(15., 0.); (-15., 10.); (-10., 0.); (-15., -10.); (15., 0.)]
117 |
118 |
119 |
120 | type bullet = {
121 | cnt: int;
122 | }
123 | let v_bullet = 30.
124 | (* number of tick to live *)
125 | let bullet_TTL = 20
126 |
127 | let space_bullet =
128 | circle red 2.
129 |
130 | let new_bullet ship =
131 | { pos = ship.pos; velocity = polar v_bullet ship.orientation;
132 | orientation = 0.;
133 | figure = space_bullet;
134 | xtra = { cnt = 0 }
135 | }
136 |
137 | type asteroid = {
138 | size: asteroid_size;
139 | }
140 | and asteroid_size = ALarge | AMedium | AWee
141 |
142 | let v_asteroid = 5.
143 |
144 | let random_range (low, high) =
145 | let diff = high -. low in
146 | let n = Random.float diff in
147 | n +. low
148 |
149 | let random_range_int (low, high) =
150 | let diff = abs (high - low) + 1 in
151 | let n = Random.int diff in
152 | n + low
153 |
154 | let space_asteroid () =
155 | let corners = random_range (4., 8.) in
156 | let increment_angle = Basics.pi2 /. corners in
157 | let rec aux angle =
158 | if angle >= Basics.pi2
159 | then []
160 | else polar (random_range (30., 50.)) angle
161 | ::aux (angle +. increment_angle)
162 | in
163 | let pts = aux increment_angle |> List.map (fun pt ->
164 | float pt.x, float pt.y
165 | ) in
166 | (* Common.pr2_gen pts; *)
167 | polygon (Color.Rgb (100, 100, 100)) pts
168 |
169 | let new_asteroid screen =
170 | let pos = {
171 | x = int_of_float (random_range (screen.left, screen.right));
172 | y = int_of_float (random_range (screen.bottom, screen.top));
173 | } in
174 | let velocity = {
175 | x = int_of_float (random_range (-. v_asteroid, v_asteroid));
176 | y = int_of_float (random_range (-. v_asteroid, v_asteroid));
177 | } in
178 | { pos; velocity; orientation = 0.;
179 | figure = space_asteroid ();
180 | xtra = { size = ALarge };
181 | }
182 |
183 | type state = Play | Stop
184 |
185 | type model = {
186 | ship: ship obj;
187 | bullets: bullet obj list;
188 | asteroids: asteroid obj list;
189 |
190 | state: state;
191 | last_tick: float;
192 | }
193 |
194 | (* 30 ms in original program *)
195 | let tick = 0.030
196 |
197 | let initial_model = {
198 | ship = {
199 | pos = { x = 0; y = 0 };
200 | velocity = { x = 0; y = 0};
201 | figure = space_ship blue;
202 | orientation = Basics.pi /. 2.;
203 | xtra = {
204 | thrust = 0.;
205 | h_acceleration = 0.;
206 | }
207 | };
208 | bullets = [];
209 | asteroids = [
210 | new_asteroid initial_computer.screen;
211 | new_asteroid initial_computer.screen;
212 | new_asteroid initial_computer.screen;
213 | new_asteroid initial_computer.screen;
214 | new_asteroid initial_computer.screen;
215 | ];
216 | state = Play;
217 | last_tick = Unix.gettimeofday();
218 | }
219 |
220 | (*****************************************************************************)
221 | (* Collision detection *)
222 | (*****************************************************************************)
223 |
224 | let (resolved_shape_of_obj: 'a obj -> resolved_shape) =
225 | fun { figure = _; pos; orientation = _; _ } ->
226 | (* TODO *)
227 | Circle (pos, 10.)
228 |
229 | let ship_crashed model =
230 | let ship = model.ship in
231 | let shp1 = resolved_shape_of_obj ship in
232 | let shapes = model.asteroids |> List.map resolved_shape_of_obj in
233 | shapes |> List.exists (fun shp2 -> intersect shp1 shp2)
234 |
235 | let directions v =
236 | let n = 1 + Random.int 3 in
237 | let rec aux n =
238 | if n = 0
239 | then []
240 | else
241 | { x = v.x + random_range_int (- v.x, v.x);
242 | y = v.y + random_range_int (- v.y, v.y);
243 | }::aux (n - 1)
244 | in
245 | aux n
246 |
247 | let explode a dirs =
248 | match a.xtra.size with
249 | | ALarge ->
250 | dirs |> List.map (fun velocity ->
251 | { a with figure = a.figure |> scale 0.5; velocity;
252 | xtra = { size = AMedium }
253 | })
254 | | AMedium ->
255 | dirs |> List.map (fun velocity ->
256 | { a with figure = a.figure |> scale 0.75; velocity;
257 | xtra = { size = AWee }})
258 | | AWee -> []
259 |
260 |
261 | let (check_asteroids: model -> asteroid obj list) =
262 | fun model ->
263 | let bullets = model.bullets |> List.map (fun x -> x.pos) in
264 | model.asteroids |> List.map (fun a ->
265 | let shp = resolved_shape_of_obj a in
266 | if bullets |> List.exists (fun p -> contains shp p)
267 | then
268 | let dirs = directions a.velocity in
269 | explode a dirs
270 | else [a]
271 | ) |> List.flatten
272 |
273 |
274 | (*****************************************************************************)
275 | (* View *)
276 | (*****************************************************************************)
277 |
278 | (* todo: resolved_shape_of_obj at some point *)
279 | let (shape_of_obj: 'a obj -> shape) =
280 | fun { figure; pos; orientation; _ } ->
281 | figure
282 | |> rotate (Basics.radians_to_degrees orientation)
283 | |> move (float pos.x) (float pos.y)
284 |
285 | let view model =
286 | shape_of_obj model.ship ::
287 | (List.map shape_of_obj model.bullets) @
288 | (List.map shape_of_obj model.asteroids)
289 |
290 | (*****************************************************************************)
291 | (* Update *)
292 | (*****************************************************************************)
293 |
294 | type msg =
295 | | Tick of float
296 |
297 | | MoveLeft
298 | | MoveRight
299 | | StopMove
300 |
301 | | Accelerate of bool
302 |
303 | | Shoot
304 |
305 | | Noop
306 |
307 | let msg_of_key_down = function
308 | | "ArrowLeft" -> MoveLeft
309 | | "ArrowRight" -> MoveRight
310 | | "ArrowUp" -> Accelerate true
311 | | "space" -> Shoot
312 | | _ -> Noop
313 |
314 | let msg_of_key_up = function
315 | | "ArrowLeft" -> StopMove
316 | | "ArrowRight" -> StopMove
317 | | "ArrowUp" -> Accelerate false
318 | | "space" -> Noop
319 | | _ -> Noop
320 |
321 |
322 | (* orig: could use modulo if the origin was not at the center on the screen *)
323 | let add_modulo_window screen pos velocity =
324 | let { x; y } = vector_add pos velocity in
325 | let x = float x in let y = float y in
326 | (* there is probably something simpler than this code ... *)
327 | let x = int_of_float (
328 | match () with
329 | | _ when x > screen.right ->
330 | x -. screen.right +. screen.left
331 | | _ when x < screen.left ->
332 | screen.right -. (screen.left -. x)
333 | | _ -> x
334 | )
335 | in
336 | let y = int_of_float (
337 | match () with
338 | | _ when y > screen.top ->
339 | y -. screen.top +. screen.bottom
340 | | _ when y < screen.bottom ->
341 | screen.top -. (screen.bottom -. y)
342 | | _ -> y
343 | )
344 | in
345 | { x; y }
346 |
347 | (* orig: this assumed to be called every tick of 30ms *)
348 | let move_ship screen
349 | ({ pos; velocity; orientation; xtra = { h_acceleration; thrust};_} as ship)=
350 | let new_velocity = vector_add (polar thrust orientation) velocity in
351 | let l = vector_length new_velocity in
352 |
353 | { ship with
354 | pos = add_modulo_window screen pos velocity ;
355 | velocity = if l > v_max then failwith "Todo" else new_velocity;
356 | orientation = orientation +. h_acceleration;
357 | }
358 |
359 | let move_bullet screen ({ pos; velocity; xtra = { cnt }; _ } as bullet) =
360 | { bullet with
361 | pos = add_modulo_window screen pos velocity;
362 | xtra = { cnt = cnt + 1 };
363 | }
364 | let move_bullets screen xs =
365 | xs
366 | |> List.map (move_bullet screen)
367 | |> List.filter (fun b -> b.xtra.cnt < bullet_TTL)
368 |
369 | let move_asteroid screen ({ pos; velocity; _ } as asteroid) =
370 | { asteroid with pos = add_modulo_window screen pos velocity; }
371 |
372 | let move_asteroids screen xs =
373 | xs
374 | |> List.map (move_asteroid screen)
375 |
376 | let update msg model =
377 | (match msg with
378 | | Noop -> model
379 | | Tick now ->
380 |
381 | let delta = now -. model.last_tick in
382 |
383 | if delta < tick || model.state = Stop
384 | then model
385 | else
386 | let model = { model with
387 | ship = move_ship initial_computer.screen model.ship;
388 | bullets = move_bullets initial_computer.screen model.bullets;
389 | asteroids = move_asteroids initial_computer.screen model.asteroids;
390 | last_tick = now;
391 | } in
392 | let asteroids = check_asteroids model in
393 | let state =
394 | if ship_crashed model
395 | then Stop
396 | else Play
397 | in
398 | { model with state; asteroids }
399 |
400 | | Shoot ->
401 | let ship = model.ship in
402 | { model with bullets = new_bullet ship::model.bullets }
403 |
404 | | MoveLeft ->
405 | (* simpler when using mutable *)
406 | model.ship.xtra.h_acceleration <- 1. *. h_delta;
407 | model
408 |
409 | | MoveRight ->
410 | model.ship.xtra.h_acceleration <- -1. *. h_delta;
411 | model
412 |
413 | | StopMove ->
414 | model.ship.xtra.h_acceleration <- 0.;
415 | model
416 | | Accelerate b ->
417 | model.ship.xtra.thrust <- if b then a_delta else 0.;
418 | model
419 | ), Cmd.none
420 |
421 | (*****************************************************************************)
422 | (* Entry point *)
423 | (*****************************************************************************)
424 |
425 | let app = { Playground.
426 | view;
427 | update;
428 | init = (fun () -> (initial_model), Cmd.none);
429 | subscriptions = (fun _ -> Sub.batch [
430 | Sub.on_animation_frame (fun x -> Tick x);
431 | Sub.on_key_down (fun key -> msg_of_key_down key);
432 | Sub.on_key_up (fun key -> msg_of_key_up key);
433 | ]);
434 | }
435 |
436 | let main =
437 | Playground_platform.run_app app
438 |
--------------------------------------------------------------------------------
/games/Pong.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 | open Basics (* float arithmetic operators by default *)
3 |
4 | (*****************************************************************************)
5 | (* Prelude *)
6 | (*****************************************************************************)
7 | (* Port of the Pong clone described at https://elm-lang.org/news/making-pong
8 | * but using OCaml instead of Elm, and using Playground instead of Collage.
9 | *
10 | * See https://en.wikipedia.org/wiki/Pong for more information on Pong.
11 | *
12 | * TODO:
13 | * - http://mathieu.agopian.info/blog/making-a-pong-game-in-elm.html (2019)
14 | * - https://github.com/einars/skapong ocaml one
15 | * - https://www.awesomeinc.org/tutorials/unity-pong/ unity! with
16 | * physics engine (friction, bounciness)
17 | * - https://en.scratch-wiki.info/wiki/Pong
18 | * - https://www.101computing.net/pong-tutorial-using-pygame-getting-started/
19 | *
20 | * TODO: extensions listed at the end of the making-pong blog post:
21 | * - dotted line mid field
22 | * - ability to pause the game
23 | * - ability to reset the game (menu somewhere? restart button between
24 | * the score)
25 | * - make ball collisions more complicated:
26 | * * when the ball hits the corner of a paddle, it changes direction
27 | * * if the ball hits a moving paddle, it adds spin to the ball, making it
28 | * rebound in a different direction
29 | * - add a second ball to the game
30 | * - write a simple AI for a paddle (easy but boring = AI that try to
31 | * stay at the same y than the ball; but could design less boring but
32 | * more fun AI too).
33 | *)
34 |
35 | (*****************************************************************************)
36 | (* Model *)
37 | (*****************************************************************************)
38 |
39 | (* was time in Pong.elm, but playground defines time as Time of posix *)
40 | type delta = float
41 |
42 | type obj = {
43 | x: number;
44 | y: number;
45 | vx: number;
46 | vy: number;
47 | }
48 |
49 | type ball = obj
50 |
51 | type player = {
52 | obj: obj;
53 | score: int;
54 | }
55 | let player x =
56 | { obj = { (* should remain constant *)
57 | x;
58 | y = 0.;
59 | (* will remain 0 *)
60 | vx = 0.;
61 | vy = 0. };
62 | score = 0 }
63 |
64 | type state = Play | Pause
65 |
66 | type game = {
67 | state: state;
68 | ball: ball;
69 | player1: player;
70 | player2: player;
71 | }
72 |
73 | (* coupling: with Playground.initial_computer.screen at 600 x 600 *)
74 | let (game_width, game_height) = (600., 400.)
75 | let (half_width, half_height) = (300., 200.)
76 |
77 | (* This means that in 1 second, the object will move 200 pixels.
78 | * note: was duplicated many times in the blog post, better to factorize.
79 | * note that this is both the velocity of the ball and the paddle, so the
80 | * paddle can not move faster than the ball :)
81 | *)
82 | let default_velocity = 200.
83 |
84 | let default_game = {
85 | state = Pause;
86 | ball = { x = 0.; y = 0.; vx = default_velocity; vy = default_velocity };
87 | (* player1 is on the left, player2 on the right *)
88 | player1 = player (-. half_width + 20.);
89 | player2 = player ( half_width - 20.);
90 | }
91 |
92 | (*****************************************************************************)
93 | (* View *)
94 | (*****************************************************************************)
95 | let pong_green = Color.Rgb (60, 100, 60)
96 | let text_green = Color.Rgb (160, 200, 160)
97 |
98 | let msg = "SPACE to start, w/s and up/down to move"
99 |
100 | let display_obj obj shape =
101 | shape |> move (obj.x) (obj.y)
102 |
103 | let view _computer (game, _last_tick) =
104 | [ rectangle pong_green game_width game_height;
105 |
106 | display_obj game.ball (oval white 15. 15.);
107 |
108 | display_obj game.player1.obj (rectangle white 10. 40.);
109 | display_obj game.player2.obj (rectangle white 10. 40.);
110 |
111 | (let s = Printf.sprintf "%d %d" game.player1.score game.player2.score in
112 | words text_green s |> scale 10. |> move 0. (game_height / 2. - 40.));
113 |
114 | (let s = if game.state = Play then "" else msg in
115 | words text_green s |> scale 2. |> move 0. (40. - game_height / 2.));
116 | ]
117 |
118 | (*****************************************************************************)
119 | (* Update *)
120 | (*****************************************************************************)
121 | (* are n and m near each other, specifically are they within c of each other *)
122 | let near n c m =
123 | m >= n - c && m <= n + c
124 |
125 | (* Is the ball within a paddle?
126 | * coupling: 8 is (a little more than) half of oval width (15) of the ball
127 | * and 20 is half the height of the player paddle (40)
128 | *)
129 | let (within: ball -> player -> bool) = fun ball player ->
130 | near player.obj.x 8. ball.x &&
131 | near player.obj.y 20. ball.y
132 |
133 | (* change the direction of a velocity (vx, or vy) based on collisions *)
134 | let (step_v: number -> bool -> bool -> number) =
135 | fun v lower_collision upper_collision ->
136 | match () with
137 | (* bottom or left collision *)
138 | | _ when lower_collision -> abs_float v
139 | (* top or right collision *)
140 | | _ when upper_collision -> -. (abs_float v)
141 | | _ -> v
142 |
143 | let (step_obj: delta -> obj -> obj) = fun t ({ x; y; vx; vy} as obj) ->
144 | { obj with x = x + vx * t; y = y + vy * t }
145 |
146 | (* move a ball forward, detecting collisions with either paddle *)
147 | let (step_ball: delta -> ball -> player -> player -> ball) =
148 | fun t ({x = _; y; vx; vy} as ball) player1 player2 ->
149 | (* put back at the center of the screen when reach player side *)
150 | if not (near 0. half_width ball.x)
151 | then { ball with x = 0.; y = 0. }
152 | else
153 | step_obj t { ball with
154 | vx = step_v vx (within ball player1) (within ball player2);
155 | (* coupling: 7. =~ half size of ball *)
156 | vy = step_v vy (y < -. half_height + 7.) (y > half_height - 7.);
157 | }
158 |
159 | (* step a player forward, making sure it does not fly off the screen *)
160 | let (step_player: delta -> number -> int -> player -> player) =
161 | fun t dir points player ->
162 | let obj' =
163 | (* bugfix: vy here! not vx *)
164 | step_obj t { player.obj with vy = dir * default_velocity } in
165 | let y' = Basics.clamp (-. half_height + 22.) (half_height - 22.) obj'.y in
166 | let score' = player.score +.. points in
167 | { obj = { obj' with y = y'}; score = score' }
168 |
169 | type input = {
170 | space: bool;
171 |
172 | (* -1, 0, 1 *)
173 | paddle1: number;
174 | paddle2: number;
175 |
176 | delta: delta;
177 | }
178 |
179 | let input_of_computer computer =
180 | let kbd = computer.keyboard in
181 | { space = kbd.kspace;
182 | (* bugfix: player2 is on the right, so the arrows or for player2 *)
183 | paddle1 = to_y2 kbd;
184 | paddle2 = to_y kbd;
185 | delta = 0.;
186 | }
187 |
188 | let (step_game: input -> game -> game) = fun input game ->
189 | let { space; paddle1; paddle2; delta} = input in
190 | let {state; ball; player1; player2} = game in
191 |
192 | (* ball on the right of player2 => score for player 1 *)
193 | let score1 = if ball.x > half_width then 1 else 0 in
194 | (* ball on left of player1 => score for player 2 *)
195 | let score2 = if ball.x < -. half_width then 1 else 0 in
196 |
197 | let state' =
198 | match () with
199 | | _ when space -> Play
200 | | _ when score1 <> score2 -> Pause
201 | | _ -> state
202 | in
203 | let ball' =
204 | if state = Pause
205 | then ball
206 | else step_ball delta ball player1 player2
207 | in
208 | let player1' = step_player delta paddle1 score1 player1 in
209 | let player2' = step_player delta paddle2 score2 player2 in
210 | { state = state'; ball = ball'; player1 = player1'; player2 = player2' }
211 |
212 | let update computer (game, last_tick) =
213 | let input = input_of_computer computer in
214 | let (Time now) = computer.time in
215 | let delta = now - last_tick in
216 | let game' = step_game { input with delta } game in
217 | game', now
218 |
219 | (*****************************************************************************)
220 | (* Entry point *)
221 | (*****************************************************************************)
222 |
223 | let app =
224 | game view update (default_game, Unix.gettimeofday())
225 |
226 | let main =
227 | Playground_platform.run_app app
228 |
--------------------------------------------------------------------------------
/games/Snake.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | (*****************************************************************************)
4 | (* Prelude *)
5 | (*****************************************************************************)
6 | (* Port of the Snake clone https://github.com/amarantedaniel/snek,
7 | * but using OCaml instead of Elm, and using Playground instead of HTML/SVG.
8 | *
9 | * See https://en.wikipedia.org/wiki/Snake_(video_game_genre) for more info.
10 | *
11 | * TODO:
12 | * - two players (like in original Snake game called Blockade)
13 | * - display score
14 | * - accelerate games as times goes
15 | * - high score table
16 | *)
17 |
18 | (*****************************************************************************)
19 | (* Model *)
20 | (*****************************************************************************)
21 |
22 | (* The origin of the grid (0, 0) is at the bottom left of the screen.
23 | * This is different from the coordinate system of Playground where the
24 | * origin is at the center of the screen, but it allows to use 'mod'
25 | * to easily move the snake around the edges.
26 | *)
27 | type position = (int * int) (* x, y *)
28 |
29 | type grid_size = {
30 | g_width: int;
31 | g_height: int;
32 | }
33 | (* less: could be changed *)
34 | let grid_size = { g_width = 20; g_height = 20 }
35 |
36 | let cell_size screen =
37 | int_of_float screen.width / grid_size.g_width
38 |
39 | (* TODO: do not return a position already used by the snake *)
40 | let random_position () =
41 | (Random.int (grid_size.g_width - 1), Random.int (grid_size.g_height - 1))
42 |
43 | type direction = Up | Down | Left | Right
44 |
45 | (* using mutable so easier to update subparts of the model *)
46 | type snake = {
47 | mutable head: position;
48 | mutable body: position list;
49 | mutable direction: direction;
50 | }
51 | let initial_snake = {
52 | head = (3, 0);
53 | body = [(2, 0); (1, 0); (1, 0)];
54 | direction = Right;
55 | }
56 |
57 |
58 | type model = {
59 | snake: snake;
60 | mutable food: position;
61 | mutable game_over: bool;
62 | mutable last_tick: Time.posix;
63 | }
64 | let initial_model = {
65 | snake = initial_snake;
66 | food = (grid_size.g_width / 2, grid_size.g_height / 2);
67 | game_over = false;
68 | last_tick = 0.;
69 | }
70 |
71 | (*****************************************************************************)
72 | (* Helpers *)
73 | (*****************************************************************************)
74 |
75 | let rec list_init = function
76 | | [] -> raise Not_found
77 | | [ _x ] -> []
78 | | x :: y :: xs -> x :: list_init (y :: xs)
79 |
80 | (*****************************************************************************)
81 | (* View *)
82 | (*****************************************************************************)
83 | let f = float
84 | let i = int_of_float
85 | let smaller size = f size *. 0.90
86 |
87 | let movei a b shape = move (f a) (f b) shape
88 |
89 | (* TODO: this currently assumes a square screen *)
90 | let translate (x,y) screen shape =
91 | let cell_size = cell_size screen in
92 | shape
93 | |> move screen.left screen.bottom
94 | |> movei (x * cell_size) (y * cell_size)
95 | |> movei (cell_size / 2) (cell_size / 2)
96 |
97 |
98 | let view_background screen =
99 | [rectangle (Color.Hex "#8cbf00") screen.width screen.height]
100 |
101 | let view_food screen pos =
102 | let size = cell_size screen in
103 | let radius = size / 3 in
104 | [circle gray (f radius) |> translate pos screen;
105 | circle black (smaller radius) |> translate pos screen;
106 | ]
107 |
108 | let view_snake_part screen pos =
109 | let size = cell_size screen in
110 | [square gray (f size) |> translate pos screen;
111 | square black (smaller size) |> translate pos screen;
112 | ]
113 |
114 | let view_snake screen snake =
115 | List.map (view_snake_part screen) (snake.head::snake.body) |> List.flatten
116 |
117 | let view_game_over _screen =
118 | [ words red "GAME OVER" |> scale 10. ]
119 |
120 | let view computer model =
121 | let screen = computer.screen in
122 |
123 | view_background screen @
124 | view_snake screen model.snake @
125 | view_food screen model.food @
126 | (if model.game_over then view_game_over screen else [])
127 |
128 |
129 | (*****************************************************************************)
130 | (* Update *)
131 | (*****************************************************************************)
132 | let compute_new_head snake =
133 | let (x, y) = snake.head in
134 | let h = grid_size.g_height in
135 | let w = grid_size.g_width in
136 | match snake.direction with
137 | | Up -> (x, (y + 1 ) mod h)
138 | | Down -> (x, (y - 1 + h) mod h)
139 | | Right -> ((x + 1) mod w, y)
140 | | Left -> ((x - 1 + w) mod w, y)
141 |
142 | let update_direction kbd snake =
143 | let new_dir =
144 | match () with
145 | | _ when kbd.kup -> Up
146 | | _ when kbd.kdown -> Down
147 | | _ when kbd.kleft -> Left
148 | | _ when kbd.kright -> Right
149 | | _ -> snake.direction
150 | in
151 | let new_dir =
152 | match new_dir, snake.direction with
153 | (* invalid transitions *)
154 | | Left, Right | Right, Left
155 | | Up, Down | Down, Up
156 | -> snake.direction
157 | | x, _ -> x
158 | in
159 | snake.direction <- new_dir
160 |
161 | let update computer model =
162 | let (Time now) = computer.time in
163 | (* operate by side effect on the model; simpler *)
164 | if now -. model.last_tick > 0.5
165 | then begin
166 | model.last_tick <- now;
167 | let snake = model.snake in
168 | let new_head = compute_new_head snake in
169 | let ate_food = new_head = model.food in
170 | let new_body =
171 | if ate_food
172 | then snake.body
173 | else list_init snake.body
174 | in
175 | snake.body <- snake.head::new_body;
176 | snake.head <- new_head;
177 | if ate_food
178 | then model.food <- random_position ();
179 | model.game_over <- List.mem new_head new_body;
180 | end;
181 | update_direction computer.keyboard model.snake;
182 |
183 | model
184 |
185 | (*****************************************************************************)
186 | (* Entry point *)
187 | (*****************************************************************************)
188 |
189 | let app =
190 | game view update initial_model
191 |
192 | let main =
193 | Random.self_init ();
194 | Playground_platform.run_app app
195 |
--------------------------------------------------------------------------------
/games/Tetris.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | (*****************************************************************************)
4 | (* Prelude *)
5 | (*****************************************************************************)
6 | (* Port of the Tetris clone https://github.com/w0rm/elm-flatris
7 | * (itself a clone of https://github.com/skidding/flatris,
8 | * itself a clone of the venerable Tetris),
9 | * but using OCaml instead of Elm, and using Playground instead of HTML/SVG.
10 | *
11 | * See https://en.wikipedia.org/wiki/Tetris for more information on Tetris.
12 | *
13 | * extensions I've added:
14 | * - space key to drop to the bottom the piece
15 | *
16 | * TODO finish porting of elm-flatris:
17 | * - check end game
18 | * - do not display piece when out of the well
19 | * - handle Pause/Resume/Stopped
20 | * - more complex input management where keeping the key pressed has
21 | * an effect, rather that forcing the user to keyup
22 | * (called confusingly "animation")
23 | * - accelerate Tick as you clear more lines, manage "level" score
24 | * TODO:
25 | * - sound when line cleared!
26 | *)
27 |
28 | (*****************************************************************************)
29 | (* Grid and pieces (model part 1) *)
30 | (*****************************************************************************)
31 | type color = Color.t
32 |
33 | (* pad: I introduced this type.
34 | * The origin (0, 0) in the grid is at the top left corner of the tetris "well".
35 | * Pos can also have a negative y, meaning it is not yet visible.
36 | *)
37 | type pos = {x: int; y: int }
38 |
39 |
40 | (* orig: was polymorphic, but always use with color, so simpler to hardcode *)
41 | type cell = {
42 | color: color;
43 | pos: pos;
44 | }
45 |
46 | (* an empty cell is represented as a non-existing cell.
47 | * alt: cell option array.
48 | *)
49 | type grid = cell list
50 |
51 | let empty_grid = []
52 |
53 |
54 | (* pad: I introduced this type.
55 | * note that x and y are between 0 and 3 for a piece.
56 | *)
57 | type piece = grid
58 |
59 | (*****************************************************************************)
60 | (* General Helpers *)
61 | (*****************************************************************************)
62 | (* those functions were in my common2.ml before, could put in a core/List_.ml *)
63 |
64 | let sum xs = List.fold_left ( + ) 0 xs
65 |
66 | let foldl1 p xs =
67 | match xs with
68 | | x :: xs -> List.fold_left p x xs
69 | | [] -> failwith "foldl1: empty list"
70 |
71 | let maximum l = foldl1 max l
72 |
73 | let exclude p xs = List.filter (fun x -> not (p x)) xs
74 |
75 | (*****************************************************************************)
76 | (* Helpers *)
77 | (*****************************************************************************)
78 |
79 | let (center_of_mass: piece -> pos) = fun piece ->
80 | let len = float (List.length piece) in
81 | let xs = piece |> List.map (fun cell -> cell.pos.x) in
82 | let ys = piece |> List.map (fun cell -> cell.pos.y) in
83 | { x = Basics.round (float (sum xs) /. len);
84 | y = Basics.round (float (sum ys) /. len);
85 | }
86 |
87 | let (init_position: int -> piece -> pos) = fun wid piece ->
88 | let {x; _} = center_of_mass piece in
89 | let y = piece |> List.map (fun cell -> cell.pos.y) |> maximum in
90 | (* -y -1 to set as non visible the bottom of the piece *)
91 | { x = wid / 2 - x;
92 | y = - y - 1
93 | }
94 |
95 | let (size: piece -> (int * int)) = fun piece ->
96 | let xs = piece |> List.map (fun cell -> cell.pos.x) in
97 | let ys = piece |> List.map (fun cell -> cell.pos.y) in
98 | let dim zs = 1 + maximum zs in
99 | dim xs, dim ys
100 |
101 | (* put the piece in the grid *)
102 | let rec (stamp: pos -> piece -> grid -> grid) = fun { x; y} piece grid ->
103 | match piece with
104 | | [] -> grid
105 | | cell::rest ->
106 | let newpos = { x = cell.pos.x + x; y = cell.pos.y + y } in
107 | (* todo: need exclude from grid cells with pos = newpos? *)
108 | stamp { x; y } rest ({ cell with pos = newpos }::grid)
109 |
110 | (* check if the piece collide with the grid in the well (width x height) *)
111 | let rec collide ({x; y} as pos) piece (width, height) grid =
112 | match piece with
113 | | [] -> false
114 | | cell::rest ->
115 | let x = cell.pos.x + x in
116 | let y = cell.pos.y + y in
117 | x >= width || x < 0 || y >= height ||
118 | (List.mem { x; y} (grid |> List.map (fun cell -> cell.pos))) ||
119 | collide pos rest (width, height) grid
120 |
121 | (* rotate clockwise *)
122 | let (rotate: piece -> piece) = fun piece ->
123 | let { x; y} = center_of_mass piece in
124 |
125 | piece |> List.map (fun cell ->
126 | let pos = cell.pos in
127 | (* ???? *)
128 | { cell with pos = { x = 1 + y - pos.y; y = - x + y + pos.x } }
129 | )
130 |
131 | let rec full_line_opt width grid =
132 | match grid with
133 | | [] -> None
134 | | cell::_ ->
135 | let line_y = cell.pos.y in
136 | let same_line, other =
137 | grid |> List.partition (fun { pos; _} -> pos.y = line_y) in
138 | if List.length same_line = width
139 | then Some line_y
140 | else full_line_opt width other
141 |
142 | let rec clear_lines width grid =
143 | match full_line_opt width grid with
144 | | None -> grid, 0
145 | | Some line_y ->
146 | let cleared_grid = grid |> exclude (fun {pos; _} -> pos.y = line_y)in
147 | let (above, below) =
148 | cleared_grid |> List.partition (fun {pos; _} -> pos.y < line_y) in
149 | let dropped_above =
150 | above |> List.map (fun cell ->
151 | { cell with pos = { x = cell.pos.x; y = cell.pos.y + 1 } }
152 | ) in
153 | let (new_grid, nb_lines) = clear_lines width (dropped_above @ below) in
154 | (new_grid, nb_lines + 1)
155 |
156 | let (from_list: color -> (int * int) list -> grid) = fun color xs ->
157 | xs |> List.map (fun (x, y) -> { color; pos = {x ; y} })
158 |
159 | let (tetriminos: piece list) = [
160 | (* xxxx
161 | *)
162 | Color.Rgb (60, 199, 214), [(0, 0); (1, 0); (2, 0); (3, 0)];
163 | (* xx
164 | * xx
165 | *)
166 | Color.Rgb (251, 180, 20), [(0, 0); (1, 0); (0, 1); (1, 1)];
167 | (* xxx
168 | * x
169 | *)
170 | Color.Rgb (176, 68, 151), [(1, 0); (0, 1); (1, 1); (2, 1)];
171 | (* xxx
172 | * x
173 | *)
174 | Color.Rgb (57, 147, 208), [(0, 0); (0, 1); (1, 1); (2, 1)];
175 | (* xxx
176 | * x
177 | *)
178 | Color.Rgb (237, 101, 47), [(2, 0); (0, 1); (1, 1); (2, 1)];
179 | (* xx
180 | * xx
181 | *)
182 | Color.Rgb (149, 196, 61), [(1, 0); (2, 0); (0, 1); (1, 1)];
183 | (* xx
184 | * xx
185 | *)
186 | Color.Rgb (232, 65, 56), [(0, 0); (1, 0); (1, 1); (2, 1)];
187 | ] |> List.map (fun (color, xs) -> from_list color xs)
188 |
189 | let random_tetrimino () =
190 | let len = List.length tetriminos in
191 | let n = Random.int len in
192 | List.nth tetriminos n
193 |
194 | (*****************************************************************************)
195 | (* Model *)
196 | (*****************************************************************************)
197 |
198 | (* todo? need Stopped and Paused? *)
199 | type state =
200 | | Stopped
201 | | Playing
202 | | Paused
203 |
204 | (* less: animationState? input *)
205 | type model = {
206 | (* screen size (= Playground.initial_computer.screen) *)
207 | (* size: number * number; *)
208 |
209 | (* grid dimension *)
210 | width: int;
211 | height: int;
212 |
213 | (* current state of the grid *)
214 | grid: grid;
215 |
216 | (* current piece *)
217 | active: piece;
218 | (* leftest highest part of the piece.
219 | * using a float for y allows to handle speed of drop *)
220 | position: (int * float);
221 | (* next piece *)
222 | next: piece;
223 |
224 | score: int;
225 | lines: int;
226 |
227 | state: state;
228 |
229 | last_tick: float;
230 | }
231 |
232 | let spawn_tetrimino model =
233 | let active = model.next in
234 | let next = random_tetrimino () in
235 | let {x; y} = init_position model.width active in
236 | { model with next; active; position = (x, float y) }
237 |
238 | let _init = Random.self_init ()
239 |
240 | let initial_model = spawn_tetrimino {
241 |
242 | (* coupling: Playground.initial_computer.screen *)
243 | (* size = (600., 600.); *)
244 |
245 | width = 10;
246 | height = 20;
247 |
248 | score = 0;
249 | lines = 0;
250 |
251 | grid = empty_grid;
252 |
253 | active = empty_grid;
254 | position = (0, 0.);
255 | next = random_tetrimino ();
256 |
257 | state = Stopped;
258 |
259 | last_tick = Unix.gettimeofday();
260 | }
261 |
262 | (*****************************************************************************)
263 | (* View *)
264 | (*****************************************************************************)
265 | let fl = float
266 |
267 | (* in pixels, height = 20 * 30 = 600 < 768 height in initial_computer.screen *)
268 | let cell_size = 30
269 |
270 | let move_box {width; height; _ } { x; y } shape =
271 | shape
272 | (* go to (0, 0), top left corner of the well *)
273 | |> move_up (fl (height * cell_size / 2))
274 | |> move_left (fl (width * cell_size / 2))
275 | (* now move the piece to (x, y) *)
276 | |> move_right (fl (x * cell_size + cell_size / 2))
277 | |> move_down (fl (y * cell_size + cell_size / 2))
278 |
279 | let render_box model { color; pos } =
280 | square color (fl cell_size) |> move_box model pos
281 |
282 | let render_well
283 | ({ width; height; position = (posx, posy); active; grid; _ } as model) =
284 | let final_grid =
285 | stamp { x = posx; y = int_of_float (floor posy) } active grid
286 | in
287 | [rectangle (Color.Rgb (236, 240, 241))
288 | (fl (width * cell_size))
289 | (fl (height * cell_size))
290 | ] @
291 | (final_grid |> List.map (render_box model)) @
292 | []
293 |
294 | let render_panel { width; height; score; lines; next; _ } =
295 | let move x =
296 | (* got to top right area *)
297 | x |> move_up (fl (height * cell_size / 2 - 2 * cell_size))
298 | |> move_right (fl (width * cell_size / 2 + 4 * cell_size))
299 | in
300 | (* less: bold *)
301 | let title = words (Color.Hex "#34495f") "Flatris" |> move |> scale 5. in
302 |
303 | let move x = x |> move |> move_down (fl (cell_size * 3)) in
304 | let score_label = words (Color.Hex "#bdc3c7") "Score" |> move |> scale 2. in
305 |
306 | let move x = x |> move |> move_down (fl (cell_size * 2)) in
307 | let score =
308 | words (Color.Hex "#3993d0") (string_of_int score) |> move |> scale 3. in
309 |
310 | let move x = x |> move |> move_down (fl (cell_size * 3)) in
311 | let lines_label =
312 | words (Color.Hex "#bdc3c7") "Lines Cleared" |> move |> scale 2. in
313 |
314 | let move x = x |> move |> move_down (fl (cell_size * 2)) in
315 | let lines =
316 | words (Color.Hex "#3993d0") (string_of_int lines) |> move |> scale 3. in
317 |
318 | let move x = x |> move |> move_down (fl (cell_size * 3)) in
319 | let next_label =
320 | words (Color.Hex "#bdc3c7") "Next Shape" |> move |> scale 2. in
321 |
322 | let move x = x |> move |> move_down (fl (cell_size * 2)) in
323 | let { x; _} = center_of_mass next in
324 | let next =
325 | next |> List.map (fun cell ->
326 | square cell.color (fl cell_size) |> move
327 | |> move_left (fl (x * cell_size))
328 | |> move_right (fl (cell.pos.x * cell_size))
329 | |> move_down (fl (cell.pos.y * cell_size))) in
330 |
331 |
332 | [title; score_label; score; lines_label; lines; next_label] @ next
333 |
334 | let view model =
335 | render_well model @
336 | render_panel model @
337 | []
338 |
339 | (*****************************************************************************)
340 | (* Update *)
341 | (*****************************************************************************)
342 | type msg =
343 | | Tick of float
344 |
345 | (* less: add bool to mean down/up (on/off) *)
346 | | MoveLeft
347 | | MoveRight
348 | | Rotate
349 | | Accelerate
350 | | FullDrop
351 |
352 | | Noop
353 |
354 | let msg_of_key = function
355 | | "ArrowLeft" -> MoveLeft
356 | | "ArrowRight" -> MoveRight
357 | | "ArrowDown" -> Accelerate
358 | | "ArrowUp" -> Rotate
359 | | "space" -> FullDrop
360 | | _ -> Noop
361 |
362 | let clear_lines_and_add_score model =
363 | let grid, nblines = clear_lines model.width model.grid in
364 | let bonus =
365 | match nblines with
366 | | 0 -> 0
367 | | 1 -> 100
368 | | 2 -> 300
369 | | 3 -> 500
370 | | 4 -> 800
371 | | _ -> failwith "Impossible"
372 | in
373 | { model with
374 | grid;
375 | score = model.score + bonus (* todo: level model *);
376 | lines = model.lines + nblines;
377 | }
378 |
379 | (* drop because Tick or Accelerate *)
380 | let (drop_tetrimino: model -> float -> model * bool) = fun model dy ->
381 | let (x, y) = model.position in
382 | let new_pos = (x, y +. dy) in
383 | if collide { x; y = int_of_float (floor (y +. dy)) } model.active
384 | (model.width, model.height) model.grid
385 | then
386 | let score = List.length model.active in
387 | { model with
388 | grid = stamp { x; y = int_of_float (floor y) } model.active model.grid;
389 | score = model.score + score;
390 | }
391 | |> spawn_tetrimino
392 | |> clear_lines_and_add_score
393 | |> (fun model -> model, true)
394 | else { model with position = new_pos }, false
395 |
396 | let rotate_tetrimino model =
397 | let rotated = rotate model.active in
398 | let (x, y) = model.position in
399 |
400 | (* make sure the rotated tetrimino stays in the grid *)
401 | let rec shift_pos_if_collide deltas =
402 | match deltas with
403 | | dx::rest ->
404 | if collide { x = x+dx; y = int_of_float (floor y) } rotated
405 | (model.width, model.height) model.grid
406 | then shift_pos_if_collide rest
407 | else { model with active = rotated; position = (x + dx, y) }
408 | | [] -> model
409 | in
410 | (* -2 + 2 range should be enough *)
411 | shift_pos_if_collide [0; 1; -1; 2; -2]
412 |
413 |
414 |
415 |
416 | (* less: Resize/GetViewPort *)
417 | let update msg model =
418 | (match msg with
419 | | Tick t ->
420 | let delta = t -. model.last_tick in
421 | let model = { model with last_tick = t } in
422 | let dy = delta in
423 | drop_tetrimino model dy |> fst
424 |
425 | | MoveLeft ->
426 | let (x, y) = model.position in
427 | let dx = - 1 in
428 | if collide { x = x+dx; y = int_of_float (floor y) } model.active
429 | (model.width, model.height) model.grid
430 | then model
431 | else { model with position = (x+dx, y) }
432 |
433 | | MoveRight ->
434 | let (x, y) = model.position in
435 | let dx = + 1 in
436 | if collide { x = x+dx; y = int_of_float (floor y) } model.active
437 | (model.width, model.height) model.grid
438 | then model
439 | else { model with position = (x+dx, y) }
440 |
441 | | Rotate ->
442 | rotate_tetrimino model
443 |
444 | | Accelerate ->
445 | let dy = 1. in
446 | drop_tetrimino model dy |> fst
447 |
448 | | FullDrop ->
449 | let rec aux model =
450 | let (model, at_bottom) = drop_tetrimino model 1. in
451 | if at_bottom
452 | then model
453 | else aux model
454 | in
455 | aux model
456 |
457 | | Noop -> model
458 | ),
459 | Cmd.none
460 |
461 | (*****************************************************************************)
462 | (* Entry point *)
463 | (*****************************************************************************)
464 |
465 | let app =
466 | { Playground.
467 | view;
468 | update;
469 | init = (fun () -> initial_model, Cmd.none);
470 | subscriptions = (fun _ -> Sub.batch [
471 | Sub.on_animation_frame (fun x -> Tick x);
472 | Sub.on_key_down (fun key -> msg_of_key key);
473 | ]);
474 | }
475 |
476 | let main =
477 | Playground_platform.run_app app
478 |
--------------------------------------------------------------------------------
/games/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names
3 | Snake
4 | Pong
5 | Tetris
6 | Asteroid
7 | )
8 | (libraries
9 | elm_playground
10 | elm_playground_native ; implem of virtual elm_playground
11 | )
12 | )
13 |
--------------------------------------------------------------------------------
/games/template.ml:
--------------------------------------------------------------------------------
1 | open Playground
2 |
3 | (*****************************************************************************)
4 | (* Prelude *)
5 | (*****************************************************************************)
6 | (* Port of the Pong clone XXX,
7 | * but using OCaml instead of Elm, and using Playground instead of YYY.
8 | *)
9 |
10 | (*****************************************************************************)
11 | (* Model *)
12 | (*****************************************************************************)
13 |
14 | let initial_model = ()
15 |
16 | (*****************************************************************************)
17 | (* View *)
18 | (*****************************************************************************)
19 |
20 | let view _computer _model = []
21 |
22 | (*****************************************************************************)
23 | (* Update *)
24 | (*****************************************************************************)
25 | let update _msg model =
26 | model
27 |
28 | (*****************************************************************************)
29 | (* Entry point *)
30 | (*****************************************************************************)
31 |
32 | let app =
33 | game view update initial_model
34 |
35 | let main =
36 | Playground_platform.run_app app
37 |
--------------------------------------------------------------------------------
/games_js/Asteroid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games_js/Asteroid.ml:
--------------------------------------------------------------------------------
1 | ../games/Asteroid.ml
--------------------------------------------------------------------------------
/games_js/Pong.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games_js/Pong.ml:
--------------------------------------------------------------------------------
1 | ../games/Pong.ml
--------------------------------------------------------------------------------
/games_js/Snake.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games_js/Snake.ml:
--------------------------------------------------------------------------------
1 | ../games/Snake.ml
--------------------------------------------------------------------------------
/games_js/Template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games_js/Tetris.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/games_js/Tetris.ml:
--------------------------------------------------------------------------------
1 | ../games/Tetris.ml
--------------------------------------------------------------------------------
/games_js/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names
3 | Snake
4 | Pong
5 | Tetris
6 | Asteroid
7 | )
8 | (libraries
9 | unix
10 | elm_playground
11 | elm_playground_web ; implem of virtual elm_playground
12 | )
13 | (modes js)
14 | (link_flags -no-check-prims)
15 | )
16 |
--------------------------------------------------------------------------------
/playground/Playground_platform.mli:
--------------------------------------------------------------------------------
1 | val run_app:
2 | ('a, 'b) Playground.app -> unit
3 |
--------------------------------------------------------------------------------
/playground/dune:
--------------------------------------------------------------------------------
1 | (library
2 | (public_name elm_playground)
3 | (virtual_modules Playground_platform)
4 | (wrapped false)
5 | (libraries
6 | ; commons
7 | elm_core elm_system
8 | )
9 | ; (preprocess (pps profiling.ppx))
10 | )
11 |
12 | (documentation
13 | (package elm_playground)
14 | (mld_files index))
15 |
--------------------------------------------------------------------------------
/playground/index.mld:
--------------------------------------------------------------------------------
1 | {0:top elm_playground}
2 |
3 | {1 OCaml Elm Playground }
4 |
5 | Create pictures, animations, and games with OCaml!
6 |
7 | This is a port of the excellent {{:https://github.com/evancz/elm-playground} Elm playground package} to OCaml.
8 |
9 | This is the package I wanted when I was learning programming. Start by
10 | putting shapes on screen and work up to making games. I hope this
11 | package will be fun for a broad range of ages and backgrounds!
12 |
13 | {2 Pictures}
14 |
15 | A picture is a list of shapes. For example, this picture combines a brown rectangle and a green circle to make a tree
16 | {[
17 | open Playground
18 |
19 | let app =
20 | picture [
21 | rectangle brown 40. 200.;
22 | circle green 100.
23 | |> move_up 100.;
24 | ]
25 |
26 | let main = Playground_platform.run_app app
27 | ]}
28 |
29 | Play around to get familiar with all the different {{!Playground.shapes} shapes}
30 | and {{!Playground.transformations} transformations} in the library.
31 |
32 | {2 Animations}
33 |
34 | An animation is a list of shapes that changes over time. For example,
35 | here is a spinning triangle:
36 |
37 | {[
38 | open Playground
39 |
40 | let view time = [
41 | triangle orange 50.
42 | |> rotate (spin 8. time);
43 | ]
44 |
45 | let app = animation view
46 |
47 | let main = Playground_platform.run_app app
48 | ]}
49 |
50 | It will do a full spin every 8 seconds.
51 |
52 | Maybe try making a car with spinning octogons as wheels? Try using
53 | {!Playground.wave} to move things back-and-forth? Try using
54 | {!Playground.zigzag} to fade things in-and-out?
55 |
56 | {2 Games}
57 |
58 | A game lets you use input from the mouse and keyboard to change your
59 | picture. For example, here is a square that moves around based on the
60 | arrow keys:
61 |
62 | {[
63 | open Playground
64 |
65 | let view _computer (x, y) = [
66 | square blue 40.
67 | |> move x y
68 | ]
69 |
70 | let update computer (x, y) =
71 | (x +. to_x computer.keyboard, y +. to_y computer.keyboard)
72 |
73 | let app =
74 | game view update (0., 0.)
75 |
76 | let main = Playground_platform.run_app app
77 | ]}
78 |
79 | Every game has three important parts:
80 | + [memory] - Store information. Our example stores [(x,y)] coordinates.
81 | + [update] - Update the memory based on mouse movements, key presses, etc. Our example moves the [(x,y)] coordinate around based on the arrow keys.
82 | + [view] - Turn the memory into a picture. Our example just shows one blue square at the [(x,y)] coordinate we have been tracking in memory.
83 |
84 |
85 | When you start making fancier games, you will store fancier things in
86 | memory. There is a lot of room to develop your programming skills
87 | here: Making lists, using records, creating custom types, etc.
88 |
89 | I started off trying to make Pong, then worked on games like Breakout
90 | and Space Invaders as I learned more and more. It was really fun, and
91 | I hope it will be for you as well!
92 |
93 |
94 | {2:index Index of modules}
95 |
96 | {!modules:
97 | Playground
98 | Playground_platform
99 | }
100 |
--------------------------------------------------------------------------------
/playground/native/Playground_platform.ml:
--------------------------------------------------------------------------------
1 | open Basics
2 | open Color
3 | module E = Sub
4 | open Tsdl
5 |
6 | (*****************************************************************************)
7 | (* Prelude *)
8 | (*****************************************************************************)
9 | (* Native backend of Playground using Cairo and SDL.
10 | *
11 | * history:
12 | * - use Graphics, but no keydown/keyup
13 | * - use ocaml-SDL, but initialy lack example to work with Cairo
14 | * - use TSDL+cairo
15 | *)
16 |
17 | (*****************************************************************************)
18 | (* Helpers *)
19 | (*****************************************************************************)
20 | (* was in my Commom.ml before, could move in a core/Regexp_.ml *)
21 |
22 | let spf = Printf.sprintf
23 |
24 | let ( =~ ) s re =
25 | Str.string_match (Str.regexp re) s 0
26 | let matched (i: int) (s: string) : string = Str.matched_group i s
27 | let _matched1 s = matched 1 s
28 | let _matched2 s = (matched 1 s, matched 2 s)
29 | let matched3 s = (matched 1 s, matched 2 s, matched 3 s)
30 |
31 | (*****************************************************************************)
32 | (* Image loading (independent of Playground) *)
33 | (*****************************************************************************)
34 |
35 | (* from ocurl/examples/opar.ml *)
36 | let writer _fname _conn accum data =
37 | (* show_progress fname conn; *)
38 | Buffer.add_string accum data;
39 | String.length data
40 |
41 | let save fname content =
42 | let fp = open_out_bin fname in
43 | Buffer.output_buffer fp content;
44 | close_out fp
45 |
46 | let curl_url fname url =
47 | let result = Buffer.create 16384 in
48 | let conn = Curl.init () in
49 | Curl.set_writefunction conn (writer fname conn result);
50 | Curl.set_followlocation conn true;
51 | Curl.set_url conn url;
52 | Curl.perform conn;
53 | Curl.cleanup conn;
54 | save fname result
55 |
56 |
57 | let png_file_of_url url =
58 | let image =
59 | (* copy of ImageLib_unix.openfile url but not falling back for GIF to
60 | * convert, which does not work well on my mac at least *)
61 | let ext = ImageUtil_unix.get_extension' url in
62 | let fn = Filename.temp_file "imagelib1" ("." ^ ext) in
63 | curl_url fn url;
64 | let ich = ImageUtil_unix.chunk_reader_of_path fn in
65 | let extension = ImageUtil_unix.get_extension' fn in
66 | Logs.debug (fun m -> m "reading png_file_of_url");
67 | try ImageLib.openfile ~extension ich
68 | with Image.Not_yet_implemented _ -> failwith (Printf.sprintf "PB with %s" fn)
69 | in
70 | let tmpfile =
71 | Filename.temp_file "imagelib2" ".png"
72 | (* "/tmp/imagelib.png" *)
73 | in
74 | Logs.debug (fun m -> m "saving png_file_of_url");
75 | ImageLib_unix.writefile tmpfile image;
76 | tmpfile
77 |
78 | (*****************************************************************************)
79 | (* Render (independent of Playground) *)
80 | (*****************************************************************************)
81 |
82 | let (g_cr: Cairo.context option ref) = ref None
83 | let get_cr () =
84 | match !g_cr with
85 | | None -> failwith "no cr"
86 | | Some x -> x
87 |
88 | let g_sx = ref 0
89 | let g_sy = ref 0
90 |
91 | (* for external images: url -> surface via Cairo.PNG.create *)
92 | let himages = Hashtbl.create 101
93 |
94 | (* less: save_excursion? *)
95 | let with_cr f =
96 | let cr = get_cr () in
97 | Cairo.save cr;
98 | let res = f cr in
99 | Cairo.restore cr;
100 | res
101 |
102 | let set_color cr color alpha =
103 | let (r,g,b) =
104 | match color with
105 | | Rgb (r,g,b) -> float r / 255., float g / 255., float b / 255.
106 | | Hex s ->
107 | let s = String.lowercase_ascii s in
108 | if s =~ "^#\\([a-f0-9][a-f0-9]\\)\\([a-f0-9][a-f0-9]\\)\\([a-f0-9][a-f0-9]\\)$"
109 | then
110 | let (a, b, c) = matched3 s in
111 | let f x =
112 | (("0x" ^ x) |> int_of_string |> float) / 255.
113 | in
114 | f a, f b, f c
115 | else failwith (spf "wrong color format: %s" s)
116 | in
117 | Cairo.set_source_rgba cr r g b (clamp 0. 1. alpha)
118 |
119 | let debug_coordinates cr =
120 | let sx = !g_sx in
121 | let sy = !g_sy in
122 | let (x0,y0) = Cairo.device_to_user cr 0. 0. in
123 | let (xmax, ymax) = Cairo.device_to_user cr (float sx) (float sy) in
124 | Logs.debug (fun m -> m "device 0,0 => %.1f %.1f, device %d,%d => %.1f %.1f"
125 | x0 y0 sx sy xmax ymax)
126 |
127 | (* Cairo (0,0) is at the top left of the screen, in which as y goes up,
128 | * the coordinates are down on the physical screen. Elm uses a better
129 | * default where if your y goes up, then it's upper on the screen.
130 | * Here we convert the Elm coordinate system to Cairo. Note that
131 | * it's hard to use one of the rotate/translate Cairo function to emulate
132 | * that as only y need to change (maybe need to create a special matrix?)
133 | *)
134 | let convert (x, y) =
135 | x, -. y
136 |
137 |
138 | let render_transform cr x y angle s =
139 | Cairo.translate cr x y;
140 | Cairo.rotate cr (-. (Basics.degrees_to_radians angle));
141 | Cairo.scale cr s s;
142 | ()
143 |
144 |
145 | let rec ngon_points cr i n radius =
146 | if i == n
147 | then ()
148 | else begin
149 | let a = turns (float i / float n - 0.25) in
150 | let x = radius * cos a in
151 | let y = radius * sin a in
152 | (if i = 0
153 | then Cairo.move_to cr x y
154 | else Cairo.line_to cr x y
155 | );
156 | ngon_points cr (Stdlib.(+) i 1) n radius
157 | end
158 |
159 | let render_ngon hook color n radius x y angle s alpha =
160 | (*pr2_gen (x,y,n,radius);*)
161 | let (x, y) = convert (x, y) in
162 |
163 | with_cr (fun cr ->
164 | hook cr;
165 | set_color cr color alpha;
166 | render_transform cr x y angle s;
167 |
168 | ngon_points cr 0 n radius;
169 | Cairo.fill cr;
170 | )
171 |
172 |
173 | let render_oval hook color w h x y _angle _s alpha =
174 | (*pr2_gen (x,y,w,h);*)
175 |
176 | let x = x - (w / 2.) in
177 | let y = y + (h / 2.) in
178 | let (x,y) = convert (x,y) in
179 |
180 | with_cr (fun cr ->
181 | hook cr;
182 | set_color cr color alpha;
183 |
184 | (* code in cairo.mli to draw ellipsis *)
185 | Cairo.translate cr (x +. w /. 2.) (y +. h /. 2.);
186 | Cairo.scale cr (w /. 2.) (h /. 2.);
187 |
188 | Cairo.arc cr 0. 0. 1. 0. pi2;
189 | Cairo.fill cr;
190 | )
191 |
192 |
193 | let render_circle hook color radius x y angle s alpha =
194 | (*pr2_gen (x,y, radius);*)
195 | let (x,y) = convert (x,y) in
196 |
197 | with_cr (fun cr ->
198 | hook cr;
199 | set_color cr color alpha;
200 | render_transform cr x y angle s;
201 |
202 | Cairo.arc cr 0. 0. radius 0. pi2;
203 | Cairo.fill cr;
204 | )
205 |
206 | let render_polygon hook color points x y angle s alpha =
207 | let (x,y) = convert (x,y) in
208 |
209 | with_cr (fun cr ->
210 | hook cr;
211 | set_color cr color alpha;
212 | render_transform cr x y angle s;
213 |
214 | (match points with
215 | | [] -> failwith "not enough points in polygon"
216 | | (x, y)::xs ->
217 | let (x,y) = convert (x,y) in
218 | Cairo.move_to cr x y;
219 | xs |> List.iter (fun (x, y) ->
220 | let (x,y) = convert (x,y) in
221 | Cairo.line_to cr x y
222 | );
223 | Cairo.line_to cr x y;
224 | Cairo.fill cr;
225 | )
226 | )
227 |
228 |
229 | let render_rectangle hook color w h x y angle s alpha =
230 | render_polygon hook color
231 | [ (-. w / 2., h /. 2.);
232 | ( w / 2., h /. 2.);
233 | ( w / 2., -.h /. 2.);
234 | (-. w / 2., -.h /. 2.);
235 | ] x y angle s alpha
236 |
237 | let render_words hook color str x y angle s alpha =
238 | let (x,y) = convert (x,y) in
239 |
240 | with_cr (fun cr ->
241 | hook cr;
242 | set_color cr color alpha;
243 | render_transform cr x y angle s;
244 |
245 | let extent = Cairo.text_extents cr str in
246 | let tw = extent.Cairo.width in
247 | let th = extent.Cairo.height in
248 |
249 | Cairo.move_to cr (-. tw / 2.) (th / 2.);
250 | Cairo.show_text cr str;
251 | )
252 |
253 | let render_image hook w h src x y angle s _alpha =
254 | let (x,y) = convert (x,y) in
255 |
256 | let surface =
257 | try
258 | Hashtbl.find himages src
259 | with Not_found ->
260 | (* pr2_gen src; *)
261 | let file = png_file_of_url src in
262 | let surface = Cairo.PNG.create file in
263 | Hashtbl.add himages src surface;
264 | surface
265 | in
266 |
267 | with_cr (fun cr ->
268 | hook cr;
269 | render_transform cr x y angle s;
270 |
271 | Cairo.set_source_surface cr surface ~x:(-. w / 2.) ~y:(-. h / 2.);
272 | Cairo.paint cr;
273 | )
274 |
275 | (*****************************************************************************)
276 | (* Render playground *)
277 | (*****************************************************************************)
278 | open Playground
279 |
280 | (* ugly, to handle Group *)
281 | type hook = Cairo.context -> unit
282 | let empty_hook = (fun _cr -> ())
283 |
284 | let rec (render_shape: hook -> shape -> unit) =
285 | fun hook { x; y; angle; scale; alpha; form} ->
286 | match form with
287 | | Circle (color, radius) ->
288 | render_circle hook color radius x y angle scale alpha
289 | | Oval (color, width, height) ->
290 | render_oval hook color width height x y angle scale alpha
291 | | Rectangle (color, width, height) ->
292 | render_rectangle hook color width height x y angle scale alpha
293 | | Ngon (color, n, radius) ->
294 | render_ngon hook color n radius x y angle scale alpha
295 | | Polygon (color, points) ->
296 | render_polygon hook color points x y angle scale alpha
297 | | Words (color, str) ->
298 | render_words hook color str x y angle scale alpha
299 | | Image (w, h, src) ->
300 | render_image hook w h src x y angle scale alpha
301 | | Group xs ->
302 | (* TODO: alpha *)
303 | let hook = (fun cr ->
304 | hook cr;
305 | let (x, y) = convert (x, y) in
306 | render_transform cr x y angle scale
307 | ) in
308 | List.iter (render_shape hook) xs
309 |
310 | let (render: shape list -> unit) = fun shapes ->
311 | List.iter (render_shape empty_hook) shapes
312 |
313 |
314 | (*****************************************************************************)
315 | (* FPS (using Cairo) *)
316 | (*****************************************************************************)
317 |
318 | module Fps = struct
319 | (* was in cairo/examples/graphics_demo.ml *)
320 | let lastfps = ref (Unix.gettimeofday ())
321 | let frames = ref 0
322 | let fps = ref 0.
323 |
324 | let update_fps () =
325 | let t = Unix.gettimeofday () in
326 | let dt = t -. !lastfps in
327 | if dt > 0.5 then (
328 | fps := float !frames /. dt;
329 | frames := 0;
330 | lastfps := t
331 | );
332 | incr frames
333 |
334 | let draw_fps cr width height =
335 | Cairo.set_source_rgba cr 0. 0. 0. 1.;
336 | Cairo.move_to cr (0.05 *. width) (0.95 *. height);
337 | Cairo.show_text cr (Printf.sprintf "%gx%g -- %.0f fps" width height !fps)
338 | end
339 |
340 | (*****************************************************************************)
341 | (* Run app *)
342 | (*****************************************************************************)
343 |
344 | (* The tsdl library is a heavy user of Result, which is annoying
345 | * to check at every calls; fortunately OCaml 4.08 allow to define
346 | * monadic operators to remove some boilerplate!
347 | *)
348 | let (let*) o f =
349 | match o with
350 | | Error (`Msg msg) ->
351 | failwith (spf "TSDL error: %s" msg)
352 | | Ok x -> f x
353 |
354 | let scancode_to_keystring = function
355 | | "Left" -> "ArrowLeft"
356 | | "Right" -> "ArrowRight"
357 | | "Up" -> "ArrowUp"
358 | | "Down" -> "ArrowDown"
359 |
360 | | "Q" -> exit 0
361 | | s -> String.lowercase_ascii s
362 |
363 | let run_app app =
364 | let sx = int_of_float Playground.default_width in
365 | let sy = int_of_float Playground.default_height in
366 |
367 | let* () = Sdl.init Sdl.Init.(video + events) in
368 | let* sdl_window = Sdl.create_window ~w:sx ~h:sy "Playground using SDL+Cairo"
369 | Sdl.Window.shown in
370 | let event = Sdl.Event.create () in
371 |
372 | let* window_surface = Sdl.get_window_surface sdl_window in
373 |
374 | let pixels = Sdl.get_surface_pixels window_surface Bigarray.int32 in
375 | assert (Bigarray.Array1.dim pixels = sx *.. sy);
376 |
377 | (* less? need that? *)
378 | Bigarray.Array1.fill pixels 0xFFFFFFFFl ;
379 | let pixels =
380 | try
381 | let genarray = Bigarray.genarray_of_array1 pixels in
382 | Bigarray.reshape_2 genarray sy sx
383 | with _ ->
384 | let len = Bigarray.Array1.dim pixels in
385 | failwith (spf
386 | "Error while reshaping pixel array of length %d to screen size %d x %d"
387 | len sx sy)
388 | in
389 | (* Create a Cairo surface to write on the pixels *)
390 | let sdl_surface =
391 | Cairo.Image.create_for_data32 ~w:sx ~h:sy pixels
392 | in
393 | let cr = Cairo.create sdl_surface in
394 |
395 | Cairo.identity_matrix cr;
396 | g_cr := Some cr;
397 | g_sx := sx;
398 | g_sy := sy;
399 | debug_coordinates cr;
400 |
401 | let initmodel, _cmdsTODO = app.Playground.init () in
402 | let model = ref initmodel in
403 |
404 | (* typing "Q" will cause an 'exit 0' that will exit the loop *)
405 | while true do
406 | Cairo.save cr;
407 |
408 | (* reset the surface content *)
409 | Cairo.set_source_rgba cr 1. 1. 1. 1.;
410 | Cairo.paint cr;
411 |
412 | (* elm-convetion: set the origin (0, 0) in the center of the surface *)
413 | Cairo.identity_matrix cr;
414 | Cairo.translate cr (float sx / 2.) (float sy / 2.);
415 | (*debug_coordinates cr;*)
416 |
417 | (* one frame *)
418 | let time = Unix.gettimeofday() in
419 |
420 | let subs = app.Playground.subscriptions !model in
421 |
422 | let event =
423 | if Sdl.poll_event (Some event)
424 | then
425 | let event_type = Sdl.Event.get event Sdl.Event.typ in
426 | (match event_type with
427 | | x when x = Sdl.Event.mouse_motion ->
428 | let x = Sdl.Event.(get event mouse_motion_x) in
429 | let y = Sdl.Event.(get event mouse_motion_y) in
430 | let (x, y) = Cairo.device_to_user cr (float x) (float y) in
431 | let (x, y) = convert (x, y) in
432 | E.EMouseMove (int_of_float x, int_of_float y)
433 |
434 | | x when x = Sdl.Event.mouse_button_down ->
435 | E.EMouseButton (true)
436 |
437 | | x when x = Sdl.Event.mouse_button_up ->
438 | E.EMouseButton (false)
439 |
440 | | x when x = Sdl.Event.key_down ->
441 | let key = Sdl.(get_key_name Event.(get event keyboard_keycode)) in
442 | let str = scancode_to_keystring key in
443 | E.EKeyChanged (true, str)
444 |
445 | | x when x = Sdl.Event.key_up ->
446 | let key = Sdl.(get_key_name Event.(get event keyboard_keycode)) in
447 | let str = scancode_to_keystring key in
448 | E.EKeyChanged (false, str)
449 |
450 | (* default case *)
451 | | _ -> E.ETick time
452 | )
453 | else E.ETick time
454 | in
455 |
456 | let msg_opt = E.event_to_msgopt event subs in
457 | (match msg_opt with
458 | | None -> ()
459 | | Some msg ->
460 | let newmodel, _cmds = app.Playground.update msg !model in
461 | model := newmodel;
462 | );
463 |
464 | let shapes = app.Playground.view !model in
465 | render shapes;
466 |
467 | Cairo.restore cr;
468 | Fps.draw_fps cr (float sx) (float sy);
469 |
470 | (* Don't forget to flush the surface before using its content. *)
471 | Cairo.Surface.flush sdl_surface;
472 | let* () = Sdl.update_window_surface sdl_window in
473 |
474 | (* Update our fps counter. *)
475 | Fps.update_fps ();
476 | done
477 |
--------------------------------------------------------------------------------
/playground/native/dune:
--------------------------------------------------------------------------------
1 | (library
2 | (public_name elm_playground_native)
3 | (implements elm_playground)
4 | ; problems with tsdl on windows I think
5 | (libraries
6 | str
7 | logs
8 | cairo2 tsdl
9 | imagelib.unix curl
10 |
11 | elm_playground
12 | )
13 | )
14 |
--------------------------------------------------------------------------------
/playground/web/Playground_platform.ml:
--------------------------------------------------------------------------------
1 | open Basics
2 | open Playground
3 | open Color
4 | module E = Sub
5 |
6 | open Js_browser
7 |
8 | (*****************************************************************************)
9 | (* Prelude *)
10 | (*****************************************************************************)
11 | (* Web backend of Playground.
12 | *
13 | * TODO: switch to Canvas, use CanvasToCairo? so closer to native playground?
14 | * I originally used SVG because that's what elm-playground is using, but
15 | * I have pbs with the mouse coordinate and the bounding box conversion,
16 | * so maybe simpler to switch to Canvas.
17 | *)
18 |
19 | (* When set to true, we generate a new frame only when there is an event.
20 | * It makes things easier to observe with Chrome developer tools.
21 | *)
22 | let debug = ref false
23 |
24 | (*****************************************************************************)
25 | (* Helpers modules *)
26 | (*****************************************************************************)
27 |
28 | (* can also use Printf.printf I think *)
29 | let log s =
30 | Js_browser.Console.log Js_browser.console (Ojs.string_to_js s)
31 |
32 | let spf = Printf.sprintf
33 |
34 | let string_of_number x =
35 | spf "%f" x
36 |
37 | (* when using the VDROM, but tedious to use request_animation_frame
38 | * and a global keyboard handler => switch to basic Dom.
39 | *)
40 | (*
41 | module V = Vdom
42 | *)
43 |
44 | (* when using directly the DOM *)
45 | module V = struct
46 | type t = Element.t
47 |
48 | type 'a vdom = t
49 |
50 | type attr =
51 | | Attr of string * string
52 | | Style of string * string
53 |
54 | let svg_ns = "http://www.w3.org/2000/svg"
55 |
56 | let svg_elt tag ~a children =
57 | (* bugfix: for svg elt we need to pass the ns! (namespace_URI), otherwise
58 | * it will not render anything.
59 | *)
60 | let elt = Document.create_element_ns document svg_ns tag in
61 | a |> List.iter (function
62 | | Attr (k, v) ->
63 | Element.set_attribute elt k v
64 | | Style (k, v) ->
65 | Ojs.set_prop_ascii
66 | (Ojs.get_prop_ascii (Element.t_to_js elt) "style")
67 | k
68 | (Ojs.string_to_js v)
69 | );
70 | children |> List.iter (fun child ->
71 | Element.append_child elt child
72 | );
73 | elt
74 | let style s1 s2 =
75 | Style (s1, s2)
76 | let attr s v =
77 | Attr (s, v)
78 | end
79 |
80 | module Html = struct
81 | let style = V.style
82 | end
83 |
84 | module Svg = struct
85 | type 'msg t = 'msg V.vdom
86 |
87 | let svg attrs xs =
88 | V.svg_elt "svg" ~a:attrs xs
89 |
90 | let trusted_node s attrs xs =
91 | V.svg_elt s ~a:attrs xs
92 |
93 | (* !subtle! need to eta-expand. You can't factorize with
94 | * let circle = trusted_node "circle" otherwise
95 | * we don't get the general 'msg vdom type inferred but
96 | * the first call to circle in this file will bind forever the type
97 | * parameter (e.g., to `Resize of int * int).
98 | * You can see the wrongly inferred type by using ocamlc -i on
99 | * this file (you may need dune --verbose to get the full list of -I first).
100 | *)
101 | let circle a b =
102 | trusted_node "circle" a b
103 | let ellipse a b =
104 | trusted_node "ellipse" a b
105 | let rect a b =
106 | trusted_node "rect" a b
107 | let polygon a b =
108 | trusted_node "polygon" a b
109 | let image a b =
110 | trusted_node "image" a b
111 |
112 | module Attributes = struct
113 | let viewBox = V.attr "viewBox"
114 |
115 | let width = V.attr "width"
116 | let height = V.attr "height"
117 |
118 | let r = V.attr "r"
119 | let rx = V.attr "rx"
120 | let ry = V.attr "ry"
121 | let fill = V.attr "fill"
122 | let points = V.attr "points"
123 | let transform = V.attr "transform"
124 | let opacity = V.attr "opacity"
125 |
126 | let href = V.attr "href"
127 |
128 | end
129 | end
130 |
131 | (*****************************************************************************)
132 | (* Render *)
133 | (*****************************************************************************)
134 |
135 | let render_color color =
136 | match color with
137 | | Hex str -> str
138 | | Rgb (r,g,b) -> Printf.sprintf "rgb(%d,%d,%d)" r g b
139 |
140 | let render_transform x y a s =
141 | if a = 0. then
142 | if s = 1.
143 | then
144 | spf "translate(%s, %s)"
145 | (string_of_number x) (string_of_number (-. y))
146 | else
147 | spf "translate(%s, %s) scale(%s)"
148 | (string_of_number x) (string_of_number (-. y))
149 | (string_of_number s)
150 | else
151 | if s = 1.
152 | then
153 | spf "translate(%s, %s) rotate(%s)"
154 | (string_of_number x) (string_of_number (-. y))
155 | (string_of_number (-. a))
156 | else
157 | spf "translate(%s, %s) rotate(%s) scale(%s) "
158 | (string_of_number x) (string_of_number (-. y))
159 | (string_of_number (-. a))
160 | (string_of_number s)
161 |
162 | let render_rect_transform width height x y angle s =
163 | render_transform x y angle s ^
164 | spf " translate(%s, %s)"
165 | (string_of_number (-. width / 2.))
166 | (string_of_number (-. height / 2.))
167 |
168 |
169 | let render_alpha alpha =
170 | if alpha = 1.
171 | then []
172 | else [Svg.Attributes.opacity (string_of_number (clamp 0. 1. alpha))]
173 |
174 | let render_circle color radius x y angle s alpha =
175 | Svg.circle
176 | (Svg.Attributes.r (string_of_number radius) ::
177 | Svg.Attributes.fill (render_color color) ::
178 | Svg.Attributes.transform (render_transform x y angle s)::
179 | render_alpha alpha
180 | )
181 | []
182 |
183 | let render_oval color width height x y angle s alpha =
184 | Svg.ellipse
185 | (Svg.Attributes.rx (string_of_number (width / 2.)) ::
186 | Svg.Attributes.ry (string_of_number (height / 2.)) ::
187 | Svg.Attributes.fill (render_color color) ::
188 | Svg.Attributes.transform (render_transform x y angle s)::
189 | render_alpha alpha
190 | )
191 | []
192 |
193 | let render_rectangle color w h x y angle s alpha =
194 | Svg.rect
195 | (Svg.Attributes.width (string_of_number (w)) ::
196 | Svg.Attributes.height (string_of_number (h)) ::
197 | Svg.Attributes.fill (render_color color) ::
198 | Svg.Attributes.transform (render_rect_transform w h x y angle s)::
199 | render_alpha alpha
200 | )
201 | []
202 |
203 | let rec to_ngon_points i n radius str =
204 | if i == n
205 | then str
206 | else
207 | let a = turns (float_of_int i / float_of_int n - 0.25) in
208 | let x = radius * cos a in
209 | let y = radius * sin a in
210 | to_ngon_points (Stdlib.(+) i 1) n radius
211 | (spf "%s%s,%s " str (string_of_number x) (string_of_number y))
212 |
213 | let render_ngon color n radius x y angle s alpha =
214 | Svg.polygon
215 | (Svg.Attributes.points (to_ngon_points 0 n radius "") ::
216 | Svg.Attributes.fill (render_color color) ::
217 | Svg.Attributes.transform (render_transform x y angle s)::
218 | render_alpha alpha
219 | )
220 | []
221 |
222 | (* TODO *)
223 | let render_words color _str x y angle s alpha =
224 | Svg.circle
225 | (Svg.Attributes.r (string_of_number 10.) ::
226 | Svg.Attributes.fill (render_color color) ::
227 | Svg.Attributes.transform (render_transform x y angle s)::
228 | render_alpha alpha
229 | )
230 | []
231 |
232 | (* TODO *)
233 | let render_polygon color _points x y angle s alpha =
234 | Svg.circle
235 | (Svg.Attributes.r (string_of_number 10.) ::
236 | Svg.Attributes.fill (render_color color) ::
237 | Svg.Attributes.transform (render_transform x y angle s)::
238 | render_alpha alpha
239 | )
240 | []
241 |
242 | let render_image w h src x y angle s alpha =
243 | Svg.image
244 | (Svg.Attributes.href src:: (* was xlinkHref but require attributeNS *)
245 | Svg.Attributes.width (string_of_number w)::
246 | Svg.Attributes.width (string_of_number h)::
247 | Svg.Attributes.fill (render_color yellow) ::
248 | Svg.Attributes.transform (render_rect_transform w h x y angle s)::
249 | render_alpha alpha
250 | )
251 | []
252 |
253 |
254 | let (render_shape: shape -> 'msg Svg.t) =
255 | fun { x; y; angle; scale; alpha; form} ->
256 | match form with
257 | | Circle (color, radius) ->
258 | render_circle color radius x y angle scale alpha
259 | | Oval (color, width, height) ->
260 | render_oval color width height x y angle scale alpha
261 | | Rectangle (color, width, height) ->
262 | render_rectangle color width height x y angle scale alpha
263 | | Ngon (color, n, radius) ->
264 | render_ngon color n radius x y angle scale alpha
265 | | Polygon (color, points) ->
266 | render_polygon color points x y angle scale alpha
267 | | Words (color, str) ->
268 | render_words color str x y angle scale alpha
269 | | Image (w, h, src) ->
270 | render_image w h src x y angle scale alpha
271 | | Group _ ->
272 | failwith "Todo"
273 |
274 |
275 | let (render: screen -> shape list -> 'msg Svg.t) = fun screen shapes ->
276 | let w = screen.width |> string_of_number in
277 | let h = screen.height |> string_of_number in
278 | let x = screen.left |> string_of_number in
279 | let y = screen.bottom |> string_of_number in
280 |
281 | Svg.svg
282 | [Svg.Attributes.viewBox (x ^ " " ^ y ^ " " ^ w ^ " " ^ h);
283 | Html.style "position" "fixed";
284 | Html.style "top" "0";
285 | Html.style "left" "0";
286 | Svg.Attributes.width "100%";
287 | Svg.Attributes.height "100%";
288 | ]
289 | (List.map render_shape shapes)
290 |
291 | (*****************************************************************************)
292 | (* Event management *)
293 | (*****************************************************************************)
294 |
295 | let adjust_x_y x y dim screen =
296 | log (spf "%f %f" x y);
297 | log (spf "h=%f, w=%f, left=%f, right=%f, top=%f, bottom=%f"
298 | (Rect.height dim) (Rect.width dim) (Rect.left dim) (Rect.right dim)
299 | (Rect.top dim) (Rect.bottom dim));
300 |
301 | let x = x - Rect.left dim in
302 | let x = x * screen.width / Rect.width dim in
303 | let x = screen.left + x in
304 |
305 | let y = y - Rect.top dim in
306 | let y = y * screen.height / Rect.height dim in
307 | let y = screen.top - y in
308 |
309 | x, y
310 |
311 | let adjust_key key =
312 | log (spf "key = '%s'" key);
313 | match key with
314 | | " " -> "space"
315 | | _ -> key
316 |
317 | let js_event_to_event evt screen =
318 | let ty = Event.type_ evt in
319 | match ty with
320 | | "mousemove" ->
321 | let (x, y) = Event.page_x evt, Event.page_y evt in
322 |
323 | let o = Event.target evt in
324 | let elt = Element.t_of_js o in
325 | let dim = Element.get_bounding_client_rect elt in
326 |
327 | let x, y = adjust_x_y x y dim screen in
328 | Some (E.EMouseMove (int_of_float x, int_of_float y))
329 | | "mousedown" | "mouseup" ->
330 | let b = Event.buttons evt > 0 in
331 | Some (E.EMouseButton b)
332 |
333 | | "keydown" ->
334 | let key = Event.key evt in
335 | let key = adjust_key key in
336 | Some (E.EKeyChanged (true, key))
337 | | "keyup" ->
338 | let key = Event.key evt in
339 | let key = adjust_key key in
340 | Some (E.EKeyChanged (false, key))
341 |
342 | | _ -> None
343 |
344 | (*****************************************************************************)
345 | (* run_app *)
346 | (*****************************************************************************)
347 |
348 | (* when using Vdom:
349 | let (vdom_app_of_app: ('model, 'msg) Playground.app -> ('model, 'msg) V.app) =
350 | fun { Playground. init; view; update; subscriptions = _subTODO } ->
351 | V.app
352 | ~init:(
353 | let (model, _cmdsTODO) = init () in
354 | model, V.Cmd.Batch [])
355 | ~view:(fun model ->
356 | (* TODO: can change! *)
357 | let screen = Playground.to_screen 600. 600. in
358 | let shapes = view model in
359 | render screen shapes
360 | )
361 | ~update:(fun model msg ->
362 | let model, _cmds = update msg model in
363 | model, V.Cmd.Batch []
364 | )
365 | ()
366 | let run_app app =
367 | let app = vdom_app_of_app app in
368 | let run () =
369 | Vdom_blit.run app
370 | |> Vdom_blit.dom
371 | |> Element.append_child (Document.body document) in
372 | let () = Window.set_onload window run in
373 | ()
374 | *)
375 |
376 | (* when using the simple DOM *)
377 | let run_app app =
378 | Window.set_onload window (fun () ->
379 |
380 | let sx = Playground.default_width in
381 | let sy = Playground.default_height in
382 | let screen = Playground.to_screen sx sy in
383 |
384 | let (initmodel, _cmdsTODO) = app.Playground.init () in
385 | let model = ref initmodel in
386 |
387 | let process_playground_event event =
388 | let subs = app.Playground.subscriptions !model in
389 | let msg_opt = E.event_to_msgopt event subs in
390 | (match msg_opt with
391 | | None -> ()
392 | | Some msg ->
393 | let newmodel, _cmds = app.Playground.update msg !model in
394 | model := newmodel;
395 | );
396 | in
397 |
398 | (* one frame *)
399 | let rec animation_frame time =
400 | let time = time /. 1000. in
401 | (* log (spf "time: %f" time); *)
402 | let event =
403 | E.ETick time
404 | in
405 | process_playground_event event;
406 |
407 | let shapes = app.Playground.view !model in
408 | let elt = render screen shapes in
409 |
410 | let body = Document.body document in
411 | Element.remove_all_children body;
412 |
413 | Element.append_child body elt;
414 |
415 | if not !debug
416 | then Window.request_animation_frame window animation_frame;
417 | in
418 | Window.request_animation_frame window animation_frame;
419 |
420 | let on_js_event evt =
421 | let evt_opt = js_event_to_event evt screen in
422 | (match evt_opt with
423 | | None -> ()
424 | | Some event -> process_playground_event event
425 | );
426 | if !debug then Window.request_animation_frame window animation_frame;
427 | in
428 | [
429 | Event.Mousemove;
430 | Event.Mousedown;
431 | Event.Mouseup;
432 | Event.Keydown;
433 | Event.Keyup;
434 | ] |> List.iter (fun evt_kind ->
435 | Window.add_event_listener window evt_kind on_js_event true
436 | );
437 | )
438 |
--------------------------------------------------------------------------------
/playground/web/dune:
--------------------------------------------------------------------------------
1 | (library
2 | (public_name elm_playground_web)
3 | (implements elm_playground)
4 | (libraries
5 | vdom
6 | ; commons
7 |
8 | elm_playground
9 | )
10 | ;(preprocess (pps profiling.ppx))
11 | )
12 |
--------------------------------------------------------------------------------
/semgrep.jsonnet:
--------------------------------------------------------------------------------
1 |
2 | local ocaml = import 'p/ocaml';
3 |
4 | //Temporary hack to not report p/ocaml findings on semgrep libs
5 | local ocaml_rules =
6 | [
7 | r { paths: { exclude: ['libs/*', 'tools/*', 'languages/*'] } }
8 | for r in ocaml.rules
9 | ];
10 |
11 | local semgrep_rules = [
12 | {
13 | // Just an example
14 | id: 'no-open-in',
15 | match: { any: ['open_in_bin ...', 'open_in ...'] },
16 | // Same but using The old syntax:
17 | // "pattern-either": [
18 | // { pattern: "open_in_bin ..." },
19 | // { pattern: "open_in ..." },
20 | // ],
21 | languages: ['ocaml'],
22 | severity: 'ERROR',
23 | message: |||
24 | It is easy to forget to close `open_in` with `close_in`.
25 | Use `Common.with_open_infile()` or `Chan.with_open_in` instead.
26 | |||,
27 | paths: {
28 | exclude: ['common2.ml'],
29 | },
30 | },
31 | ];
32 |
33 | // ----------------------------------------------------------------------------
34 | // Skip and last-minute override
35 | // ----------------------------------------------------------------------------
36 |
37 | local todo_skipped_for_now = [
38 | //TODO? what is the fix for that?
39 | 'ocaml.lang.portability.crlf-support.broken-input-line',
40 | // too noisy
41 | 'ocaml.lang.security.hashtable-dos.ocamllint-hashtable-dos',
42 | ];
43 |
44 | local override_messages = {
45 | // Semgrep specific adjustments
46 | 'ocaml.lang.best-practice.exception.bad-reraise': |||
47 | You should not re-raise exceptions using 'raise' because it loses track
48 | of where the exception was raised originally. See commons/Exception.mli
49 | for more information.
50 | Use `Exception.catch exn` and later `Exception.raise exn` or
51 | `Exception.catch_and_reraise exn` if there is no code between the moment
52 | you catch the exn and re-raise it.
53 | |||,
54 | };
55 |
56 | // ----------------------------------------------------------------------------
57 | // Entry point
58 | // ----------------------------------------------------------------------------
59 |
60 | // TODO? Use TCB rules?
61 |
62 | local all = semgrep_rules;
63 | {
64 | rules:
65 | [
66 | if std.objectHas(override_messages, r.id)
67 | then (r { message: override_messages[r.id] })
68 | else r
69 | for r in all
70 | if !std.member(todo_skipped_for_now, r.id)
71 | ],
72 | }
73 |
--------------------------------------------------------------------------------
/skip_list.txt:
--------------------------------------------------------------------------------
1 | dir: _build
2 | dir: tests
3 |
4 |
5 |
--------------------------------------------------------------------------------
/system/Cmd.ml:
--------------------------------------------------------------------------------
1 | type 'msg t = None | Msg of 'msg
2 | let none = None
3 |
--------------------------------------------------------------------------------
/system/Sub.ml:
--------------------------------------------------------------------------------
1 | type 'msg onesub =
2 | | SubTick of (Time.posix -> 'msg)
3 | | SubMouseMove of (float * float -> 'msg)
4 | | SubMouseDown of (unit -> 'msg)
5 | | SubMouseUp of (unit -> 'msg)
6 | | SubKeyDown of (Keyboard.key -> 'msg)
7 | | SubKeyUp of (Keyboard.key -> 'msg)
8 |
9 |
10 | type 'msg t = 'msg onesub list
11 | let none = []
12 | let batch xs = (List.flatten xs)
13 |
14 | (* was in Event_.ml before *)
15 | let (on_animation_frame: (Time.posix -> 'msg) -> 'msg t) = fun f ->
16 | [SubTick f]
17 |
18 | let (on_mouse_move: (float * float -> 'msg) -> 'msg t) = fun f ->
19 | [SubMouseMove f]
20 |
21 | let (on_mouse_down: (unit -> 'msg) -> 'msg t) = fun f ->
22 | [SubMouseDown f]
23 |
24 | let (on_mouse_up: (unit -> 'msg) -> 'msg t) = fun f ->
25 | [SubMouseUp f]
26 |
27 | let (on_key_down: (Keyboard.key -> 'msg) -> 'msg t) = fun f ->
28 | [SubKeyDown f]
29 |
30 | let (on_key_up: (Keyboard.key -> 'msg) -> 'msg t) = fun f ->
31 | [SubKeyUp f]
32 |
33 |
34 |
35 |
36 | type event =
37 | | ETick of float
38 | | EMouseMove of (int * int)
39 | | EMouseButton of bool (* is_down = true *)
40 | | EKeyChanged of (bool (* down = true *) * Keyboard.key)
41 |
42 | let rec find_map_opt f = function
43 | | [] -> None
44 | | x::xs ->
45 | (match f x with
46 | | None -> find_map_opt f xs
47 | | Some x -> Some x
48 | )
49 |
50 | let event_to_msgopt event subs =
51 | match event with
52 | | ETick time ->
53 | subs |> find_map_opt (function
54 | | SubTick f -> Some (f time)
55 | | _ -> None
56 | )
57 | | EMouseMove (x, y) ->
58 | subs |> find_map_opt (function
59 | | SubMouseMove f ->
60 | Some (f (float_of_int x, float_of_int y))
61 | | _ -> None
62 | )
63 | | EMouseButton (true) ->
64 | subs |> find_map_opt (function
65 | | SubMouseDown f ->
66 | Some (f ())
67 | | _ -> None
68 | )
69 | | EMouseButton (false) ->
70 | subs |> find_map_opt (function
71 | | SubMouseUp f ->
72 | Some (f ())
73 | | _ -> None
74 | )
75 | | EKeyChanged (true, key) ->
76 | subs |> find_map_opt (function
77 | | SubKeyDown f ->
78 | Some (f key)
79 | | _ -> None
80 | )
81 | | EKeyChanged (false, key) ->
82 | subs |> find_map_opt (function
83 | | SubKeyUp f ->
84 | Some (f key)
85 | | _ -> None
86 | )
87 |
--------------------------------------------------------------------------------
/system/dune:
--------------------------------------------------------------------------------
1 | (library
2 | (public_name elm_system)
3 | (wrapped false)
4 | (libraries
5 | ; commons
6 | elm_core
7 | )
8 | ; (preprocess (pps profiling.ppx))
9 | )
10 |
--------------------------------------------------------------------------------
/system/pad.txt:
--------------------------------------------------------------------------------
1 | was in core in elm
2 |
3 |
--------------------------------------------------------------------------------
/tests/Svg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tests/Svg.ml:
--------------------------------------------------------------------------------
1 | (* This file is part of the ocaml-vdom package, released under the terms of an MIT-like license. *)
2 | (* See the attached LICENSE file. *)
3 | (* Copyright 2016 by LexiFi. *)
4 |
5 | open Vdom
6 |
7 | type model = int
8 |
9 | let update n = function
10 | | `Click -> n mod 5 + 1
11 |
12 | let init = 1
13 |
14 | let view n =
15 | div
16 | [
17 | text (string_of_int n);
18 | svg_elt "svg"
19 | ~a:[
20 | int_attr "width" (n * 20);
21 | int_attr "height" (n * 20);
22 | ]
23 | [
24 | svg_elt "circle" []
25 | ~a:[
26 | onclick (fun _ -> `Click);
27 | int_attr "cx" (n * 10);
28 | int_attr "cy" (n * 10);
29 | int_attr "r" (n * 10);
30 | attr "fill" (if n mod 2 = 0 then "green" else "blue");
31 | ]
32 | ]
33 | ]
34 |
35 | let app = simple_app ~init ~view ~update ()
36 |
37 |
38 | open Js_browser
39 |
40 | let run () = Vdom_blit.run app |> Vdom_blit.dom |> Element.append_child (Document.body document)
41 | let () = Window.set_onload window run
42 |
--------------------------------------------------------------------------------
/tests/Test_cairo_graphics.ml:
--------------------------------------------------------------------------------
1 | (* from cairo/examples/graphics_demo.ml *)
2 |
3 | (* Demo to show how one could achieve cairo drawing on a Graphics
4 | window. Note that using XLib or GTK would be *much* faster. *)
5 |
6 | open Printf
7 | open Cairo
8 |
9 | let pi2 = 8. *. atan 1.
10 |
11 | let lastfps = ref (Unix.gettimeofday ())
12 | let frames = ref 0
13 | let fps = ref 0.
14 |
15 | let update_fps () =
16 | let t = Unix.gettimeofday () in
17 | let dt = t -. !lastfps in
18 | if dt > 0.5 then (
19 | fps := float !frames /. dt;
20 | frames := 0;
21 | lastfps := t
22 | );
23 | incr frames
24 |
25 | let draw cr width height x y =
26 | let x = x -. width *. 0.5 and y = y -. height *. 0.5 in
27 | let r = 0.5 *. sqrt (x *. x +. y *. y) in
28 | set_source_rgba cr 0. 1. 0. 0.5;
29 | arc cr (0.5 *. width) (0.35 *. height) r 0. pi2;
30 | fill cr;
31 | set_source_rgba cr 1. 0. 0. 0.5;
32 | arc cr (0.35 *. width) (0.65 *. height) r 0. pi2;
33 | fill cr;
34 | set_source_rgba cr 0. 0. 1. 0.5;
35 | arc cr (0.65 *. width) (0.65 *. height) r 0. pi2;
36 | fill cr;
37 | set_source_rgba cr 1. 1. 0. 1.;
38 | move_to cr (0.05 *. width) (0.95 *. height);
39 | show_text cr (sprintf "%gx%g -- %.0f fps" width height !fps)
40 |
41 | let expose () =
42 | let sx = Graphics.size_x ()
43 | and sy = Graphics.size_y ()
44 | and mx, my = Graphics.mouse_pos () in
45 | (* Create a cairo context from a cairo surface and do our drawings
46 | on it. Note: we may cache it between expose events for
47 | incremental drawings but its creation and initialization is not
48 | the time bottleneck here. *)
49 | let cr_img = Image.create Image.RGB24 sx sy in
50 | let cr = create cr_img in
51 | draw cr (float sx) (float sy) (float mx) (float my);
52 | (* Don't forget to flush the surface before using its content. *)
53 | Surface.flush cr_img;
54 | (* Now, access the surface data and convert it to a Graphics.image
55 | that can be drawn on the Graphics window. *)
56 | let data32 = Image.get_data32 cr_img in
57 | let data_img =
58 | Array.init sy
59 | (fun y -> Array.init sx (fun x -> Int32.to_int (data32.{y, x})))
60 | in
61 | Graphics.draw_image (Graphics.make_image data_img) 0 0;
62 | Graphics.synchronize ();
63 | (* Update our fps counter. *)
64 | update_fps ()
65 |
66 | let () =
67 | Graphics.open_graph "";
68 | Graphics.auto_synchronize false;
69 | while true do
70 | expose ()
71 | done
72 |
--------------------------------------------------------------------------------
/tests/Test_ocamlsdl2.ml:
--------------------------------------------------------------------------------
1 | (* from ocamlsdl2/examples/ex_event.ml *)
2 | [@@@warning "-9"]
3 |
4 | open Sdlevent
5 | open Sdl
6 |
7 | let print_keyboard_event e st =
8 | Printf.printf "
9 | keyboard_event ((
10 | timestamp: %ld
11 | window_id: %lX
12 | state: %s / %s
13 | repeat: %d
14 | scancode: %s
15 | keycode: %s
16 | keymod: %s
17 | ))\n%!"
18 | e.ke_timestamp
19 | e.ke_window_id
20 | (Sdlevent.string_of_state e.ke_state) st
21 | e.ke_repeat
22 | (Sdlscancode.to_string e.scancode)
23 | (Sdlkeycode.to_string e.keycode)
24 | (String.concat " "
25 | (List.map Sdlkeymod.to_string e.keymod))
26 |
27 | let print_mouse_motion_event e =
28 | Printf.printf "
29 | mouse_motion_event ((
30 | timestamp: %ld
31 | window_id: %lX
32 | buttons: %s
33 | x: %d
34 | y: %d
35 | xrel: %d
36 | yrel: %d
37 | ))\n%!"
38 | e.mm_timestamp
39 | e.mm_window_id
40 | (String.concat " "
41 | (List.map string_of_int e.mm_buttons))
42 | e.mm_x
43 | e.mm_y
44 | e.mm_xrel
45 | e.mm_yrel
46 |
47 | let print_mouse_button_event e s =
48 | Printf.printf "
49 | mouse_button_event ((
50 | timestamp: %ld
51 | window_id: %lX
52 | button: %d
53 | state: %s %s
54 | x: %d
55 | y: %d
56 | ))\n%!"
57 | e.mb_timestamp
58 | e.mb_window_id
59 | e.mb_button
60 | (Sdlevent.string_of_state e.mb_state) s
61 | e.mb_x
62 | e.mb_y
63 |
64 | let print_mouse_wheel_event e =
65 | Printf.printf "
66 | mouse_wheel_event ((
67 | timestamp: %ld
68 | window_id: %lX
69 | x: %d
70 | y: %d
71 | ))\n%!"
72 | e.mw_timestamp
73 | e.mw_window_id
74 | e.mw_x
75 | e.mw_y
76 |
77 | let print_joy_axis_event e =
78 | Printf.printf "
79 | joy_axis_event ((
80 | timestamp: %ld
81 | which: %d
82 | axis: %d
83 | value: %d
84 | ))\n%!"
85 | e.ja_timestamp
86 | e.ja_which
87 | e.ja_axis
88 | e.ja_value
89 |
90 | let print_joy_button_event e s =
91 | Printf.printf "
92 | joy_button_event ((
93 | timestamp: %ld
94 | which: %d
95 | button: %d
96 | state: %s %s
97 | ))\n%!"
98 | e.jb_timestamp
99 | e.jb_which
100 | e.jb_button
101 | (Sdlevent.string_of_state e.jb_state) s
102 |
103 | let print_joy_hat_event e =
104 | Printf.printf "
105 | joy_hat_event ((
106 | timestamp: %ld
107 | which: %d
108 | hat: %d
109 | pos: \t left:%b \t right:%b \t up:%b \t down:%b
110 | # %s
111 | # %s
112 | ))\n%!"
113 | e.jh_timestamp
114 | e.jh_which
115 | e.jh_hat
116 | e.jh_pos.Hat.left
117 | e.jh_pos.Hat.right
118 | e.jh_pos.Hat.up
119 | e.jh_pos.Hat.down
120 | (Sdlhat.string_of_pos e.jh_pos)
121 | (Sdlhat.string_of_dir e.jh_dir)
122 |
123 | let print_joy_device_event e ch =
124 | Printf.printf "
125 | joy_device_event ((
126 | timestamp: %ld
127 | which: %d
128 | device_change: %s %s
129 | ))\n%!"
130 | e.jd_timestamp
131 | e.jd_which
132 | (Sdlevent.string_of_joy_device_change e.jd_change) ch
133 |
134 | let print_window_event e =
135 | Printf.printf "
136 | window_event ((
137 | timestamp: %ld
138 | window_ID: %ld
139 | kind: %s
140 | ))\n%!"
141 | e.we_timestamp
142 | e.window_ID
143 | (Sdlevent.string_of_window_event_kind e.kind)
144 |
145 |
146 | let proc_events = function
147 | | KeyDown { scancode = Sdlscancode.ESCAPE } ->
148 | print_endline "Goodbye";
149 | exit 0
150 | | KeyDown { keycode = Sdlkeycode.Escape } ->
151 | print_endline "Fuck you!";
152 | exit 0
153 | | KeyDown e -> print_keyboard_event e "down"
154 | | KeyUp e -> print_keyboard_event e "up"
155 | | Mouse_Motion e -> print_mouse_motion_event e
156 | | Mouse_Button_Down e -> print_mouse_button_event e "down"
157 | | Mouse_Button_Up e -> print_mouse_button_event e "up"
158 | | Mouse_Wheel e -> print_mouse_wheel_event e
159 | | Joy_Axis_Motion e -> print_joy_axis_event e
160 | | Joy_Button_Down e -> print_joy_button_event e "down"
161 | | Joy_Button_Up e -> print_joy_button_event e "up"
162 | | Joy_Hat_Motion e -> print_joy_hat_event e
163 | | Joy_Device_Removed e -> print_joy_device_event e "Removed"
164 | | Joy_Device_Added e -> print_joy_device_event e "Added";
165 | ignore(Sdljoystick.j_open e.jd_which);
166 | | Window_Event { kind = WindowEvent_Resized p } ->
167 | Printf.printf "### WINDOW_RESIZED => %d x %d\n" p.win_x p.win_y
168 | | Window_Event e -> print_window_event e
169 | | Quit e ->
170 | Printf.printf "Quit(timestamp:%ld)\n%!" e.quit_timestamp;
171 | Sdl.quit ();
172 | exit 0
173 | | e ->
174 | print_endline (Sdlevent.to_string e)
175 |
176 |
177 | let () =
178 | let width, height = (640, 480) in
179 | Sdl.init [`VIDEO; `JOYSTICK];
180 | at_exit print_newline;
181 | (*
182 | let window, renderer =
183 | Sdlrender.create_window_and_renderer ~width ~height
184 | in
185 | ignore (window, renderer);
186 | *)
187 | let window =
188 | Sdlwindow.create2
189 | ~title:"SDL Event"
190 | ~x:`undefined ~y:`undefined
191 | ~width ~height
192 | ~flags:[
193 | (*
194 | Sdlwindow.FullScreen;
195 | Sdlwindow.OpenGL;
196 | Sdlwindow.Shown;
197 | Sdlwindow.Hidden;
198 | Sdlwindow.Borderless;
199 | Sdlwindow.Resizable;
200 | Sdlwindow.Minimized;
201 | Sdlwindow.Maximized;
202 | Sdlwindow.Input_Grabbed;
203 | Sdlwindow.Input_Focus;
204 | Sdlwindow.Mouse_Focus;
205 | Sdlwindow.FullScreen_Desktop;
206 | Sdlwindow.Foreign;
207 | *)
208 | ]
209 | in
210 | ignore (window);
211 |
212 | let joy_num = Sdljoystick.num_joysticks () in
213 | for i = 0 to pred joy_num do
214 | ignore(Sdljoystick.j_open i);
215 | let name = Sdljoystick.name_for_index i in
216 | Printf.printf "Joy-name: %s\n" name;
217 | done;
218 |
219 | let rec event_loop () =
220 | match Sdlevent.poll_event () with
221 | | Some ev -> proc_events ev; event_loop ()
222 | | None -> ()
223 | in
224 | let rec main_loop () =
225 | event_loop ();
226 | Printf.printf ".%!";
227 | Sdltimer.delay 20;
228 | main_loop ()
229 | in
230 | main_loop ()
231 |
--------------------------------------------------------------------------------
/tests/Test_vdom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tests/Test_vdom.ml:
--------------------------------------------------------------------------------
1 | open Vdom
2 |
3 | type model = string
4 |
5 | let update _ = function
6 | | `Click ->
7 | "Click:"
8 | | `Keydown s ->
9 | "Keydown:" ^ s
10 |
11 | let init = "EMPTY"
12 |
13 | let view s =
14 | div
15 | ~a:[
16 | onkeydown (fun evt -> `Keydown (Printf.sprintf "%d" evt.which));
17 | autofocus;
18 | ]
19 | [
20 | div [text (Printf.sprintf "Content: %s" s)];
21 | div [input [] ~a:[onclick (fun _ -> `Click); type_button; value "Update"]]
22 | ]
23 |
24 | let app = simple_app ~init ~view ~update ()
25 |
26 |
27 | open Js_browser
28 |
29 | let run () = Vdom_blit.run app |> Vdom_blit.dom |> Element.append_child (Document.body document)
30 | let () = Window.set_onload window run
31 |
--------------------------------------------------------------------------------
/tests/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names
3 | ; Test_cairo_graphics
4 | Test_tsdl
5 | ; Test_ocamlsdl2
6 | )
7 | (libraries
8 | cairo2
9 | ; graphics
10 | tsdl
11 | ; sdl2
12 | )
13 | )
14 |
--------------------------------------------------------------------------------