0
0
0
0
0
├── .gitignore
├── assets
└── og.jpg
├── src
├── dom.js
├── collisions.js
├── options.js
├── results.js
├── app.js
├── Ball.js
├── index.html
└── favicon.svg
├── .editorconfig
├── LICENSE
├── README.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | .now
4 | .DS_Store
5 | public
--------------------------------------------------------------------------------
/assets/og.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/midudev/covid-19-spread-simulator/master/assets/og.jpg
--------------------------------------------------------------------------------
/src/dom.js:
--------------------------------------------------------------------------------
1 | const $ = id => document.getElementById(id)
2 |
3 | export const replayButton = $('replay')
4 | export const deathFilter = $('deaths')
5 | export const stayHomeFilter = $('stay-home')
6 | export const graphElement = $('graph')
7 | export const replayElement = $('replay')
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 | charset = utf-8
11 | indent_style = space
12 | indent_size = 2
--------------------------------------------------------------------------------
/src/collisions.js:
--------------------------------------------------------------------------------
1 | const { hypot } = Math
2 |
3 | export const calculateChangeDirection = ({ dx, dy }) => {
4 | const hyp = hypot(dx, dy);
5 | const ax = dx / hyp;
6 | const ay = dy / hyp
7 | return { ax, ay }
8 | }
9 |
10 | export const checkCollision = ({ dx, dy, diameter }) => {
11 | const distance2 = dx * dx + dy * dy
12 | return distance2 < diameter * diameter
13 | }
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Miguel Ángel Durán (@midudev)
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4 |
5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # COVID-19 (Coronavirus) spread simulator 🦠
2 |
3 | Check simulations about how confinement people could help to stop spreading Coronavirus.
4 |
5 | [Based on Washington Post Article: Why outbreaks like coronavirus spread exponentially, and how to “flatten the curve” - Washington Post](https://www.washingtonpost.com/graphics/2020/world/corona-simulator/)
6 |
7 | ## How to start
8 |
9 | Install all the project dependencies with:
10 | ```
11 | npm install
12 | ```
13 |
14 | And start the development server with:
15 | ```
16 | npm run dev
17 | ```
18 |
19 | ## Browser support
20 |
21 | This project is using EcmaScript Modules, therefore, only browsers with this compatibility will work. (Sorry Internet Explorer 11 and old Edge users).
22 |
23 | ## Next content
24 | - Customize strategies (number of static people and mortality)
25 | - Customize colors
26 | - Iframe support
27 | - I18N
28 | - New strategies
29 | - Improve the code so I don't get so ashamed. 😳
30 |
--------------------------------------------------------------------------------
/src/options.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_FILTERS = {
2 | death: false,
3 | stayHome: false
4 | }
5 |
6 | export const CANVAS_SIZE = {
7 | height: 880,
8 | width: 360
9 | }
10 |
11 | export const DESKTOP_CANVAS_SIZE = {
12 | height: 400,
13 | width: 800
14 | }
15 |
16 | export const BALL_RADIUS = 5
17 | export const COLORS = {
18 | death: '#c50000',
19 | recovered: '#D88DBC',
20 | infected: '#5ABA4A',
21 | well: '#63C8F2'
22 | }
23 |
24 | export const STATES = {
25 | infected: 'infected',
26 | well: 'well',
27 | recovered: 'recovered',
28 | death: 'death'
29 | }
30 |
31 | export const COUNTERS = {
32 | ...STATES,
33 | 'max-concurrent-infected': 'max-concurrent-infected'
34 | }
35 |
36 | export const STARTING_BALLS = {
37 | [STATES.infected]: 1,
38 | [STATES.well]: 199,
39 | [STATES.recovered]: 0,
40 | [STATES.death]: 0,
41 | 'max-concurrent-infected': 0
42 | }
43 |
44 | export const RUN = {
45 | filters: { ...DEFAULT_FILTERS },
46 | results: { ...STARTING_BALLS },
47 | tick: 0
48 | }
49 |
50 | export const MORTALITY_PERCENTATGE = 5
51 | export const SPEED = 1
52 | export const TOTAL_TICKS = 1600
53 | export const TICKS_TO_RECOVER = 500
54 | export const STATIC_PEOPLE_PERCENTATGE = 25
55 |
56 | export const resetRun = () => {
57 | RUN.results = { ...STARTING_BALLS }
58 | RUN.tick = 0
59 | }
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "coronavirus-spread-simulator",
3 | "version": "1.0.1",
4 | "private": true,
5 | "description": "Simulator about how Coronavirus spreads with free movement of people and confinement",
6 | "main": "",
7 | "scripts": {
8 | "build": "npm run create:public && npm run minify:files && npm run copy:assets",
9 | "create:public": "mkdir -p public",
10 | "minify:files": "cd src && for f in ./*; do minify $f > ../public/$f; done;",
11 | "copy:assets": "cp -r ./assets/ ./public/assets",
12 | "lint": "eslint src",
13 | "dev": "servor src index.html 1234 --browser --reload",
14 | "test": "echo \"Error: no test specified\" && exit 1"
15 | },
16 | "keywords": [],
17 | "author": "Miguel Ángel Durán García - https://midu.dev",
18 | "license": "ISC",
19 | "devDependencies": {
20 | "husky": "4.2.3",
21 | "minify": "5.1.0",
22 | "servor": "3.2.0",
23 | "standard": "14.3.3"
24 | },
25 | "husky": {
26 | "hooks": {
27 | "pre-commit": "npm run lint",
28 | "pre-push": "npm run lint"
29 | }
30 | },
31 | "eslintConfig": {
32 | "extends": "eslint-config-standard",
33 | "rules": {
34 | "indent": [
35 | "error",
36 | 2
37 | ],
38 | "max-len": [
39 | "error",
40 | {
41 | "code": 100
42 | }
43 | ]
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/results.js:
--------------------------------------------------------------------------------
1 | import {
2 | COLORS,
3 | RUN,
4 | TOTAL_TICKS,
5 | STATES,
6 | COUNTERS,
7 | resetRun
8 | } from './options.js'
9 |
10 | import {
11 | graphElement,
12 | replayElement
13 | } from './dom.js'
14 |
15 | let graphPoint = 0
16 | const matchMedia = window.matchMedia('(min-width: 800px)')
17 |
18 | let isDesktop = matchMedia.matches
19 |
20 | const domElements = Object.fromEntries(
21 | Object.keys(COUNTERS).map(state => {
22 | const el = document.getElementById(state)
23 | if (el) {
24 | el.parentNode.style = `color: ${COLORS[state]}`
25 | }
26 | return [state, document.getElementById(state)]
27 | })
28 | )
29 |
30 | const updateGraph = () => {
31 | let y = 0
32 | const rects = Object.entries(RUN.results).map(([state, count]) => {
33 | const color = COLORS[state]
34 | if (count > 0) {
35 | const percentatge = count / 200 * 50
36 | const rect = `