Mattéo Delabre
5 lat temu
31 zmienionych plików z 2837 dodań i 6709 usunięć
@ -1,44 +0,0 @@ |
|||
{ |
|||
"extends": "eslint:recommended", |
|||
|
|||
"rules": { |
|||
"no-shadow": 2, |
|||
"no-catch-shadow": 2, |
|||
"no-shadow-restricted-names": 2, |
|||
"radix": 2, |
|||
|
|||
"wrap-iife": 2, |
|||
"yoda": 2, |
|||
"semi": 2, |
|||
"indent": 2, |
|||
"camelcase": 2, |
|||
"brace-style": 2, |
|||
"comma-spacing": 2, |
|||
"comma-style": 2, |
|||
"quotes": [2, "single", "avoid-escape"], |
|||
"no-spaced-func": 2, |
|||
"space-after-keywords": 2, |
|||
"space-before-blocks": 2, |
|||
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], |
|||
"space-in-parens": 2, |
|||
"space-infix-ops": 2, |
|||
"space-return-throw-case": 2, |
|||
"space-unary-ops": 2, |
|||
"no-trailing-spaces": 0, |
|||
"no-underscore-dangle": 0 |
|||
}, |
|||
|
|||
"ecmaFeatures": { |
|||
"modules": true, |
|||
"jsx": true |
|||
}, |
|||
|
|||
"plugins": [ |
|||
"react" |
|||
], |
|||
|
|||
"env": { |
|||
"es6": true, |
|||
"browser": true |
|||
} |
|||
} |
@ -1 +1,3 @@ |
|||
*.map |
|||
node_modules |
|||
public/bundle.* |
|||
|
@ -1,56 +0,0 @@ |
|||
# Contribuer |
|||
|
|||
[Looking for the english version?](CONTRIBUTING.md) |
|||
|
|||
Merci de votre intérêt à contribuer à ce code ! |
|||
Toutes les contributions (même les plus petites) sont les bienvenues. |
|||
Pour garder une certaine cohérence dans le code, merci de suivre |
|||
ces quelques règles. |
|||
|
|||
## 1. Commit tags |
|||
|
|||
Tous les commits doivent être précédés d'emojis dans la mesure |
|||
du possible pour que la liste des commits soit plus lisible. |
|||
|
|||
| Emoji | Type de commit | |
|||
|:----------:|:---------------------------------| |
|||
| :book: | Changement dans la documentation | |
|||
| :bug: | Correction de bug | |
|||
| :ledger: | Déplacement de fichiers | |
|||
| :bulb: | Nouvelles fonctionnalités | |
|||
| :lipstick: | Correction du style de code | |
|||
|
|||
## 2. Branches |
|||
|
|||
Merci d'utiliser un nom de branche différent de |
|||
`master` pour vos pull requests, pour que l'historique |
|||
soit plus lisible. |
|||
|
|||
Par exemple, pour améliorer la documentation, vous |
|||
pourriez choisir le nom `improve-docs`. |
|||
|
|||
## 3. Conventions de style |
|||
|
|||
Le code Javascript peut être écrit suivant |
|||
[beaucoup](https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) |
|||
[de](https://github.com/airbnb/javascript) |
|||
[styles](https://github.com/felixge/node-style-guide) |
|||
[différents](https://contribute.jquery.org/style-guide/js/) |
|||
mais nous ne sommes pas aussi durs que ceux-ci. |
|||
|
|||
La règle principale est d'utiliser [ESLint](http://eslint.org/) pour |
|||
vérifier si votre code s'accorde avec nos conventions de style. |
|||
Voici quelques unes des règles : |
|||
|
|||
* utiliser le paramètre de base dans `parseInt()`; |
|||
* utiliser le *one true brace style;* |
|||
* mettre un espace après les virgules, pas d'espace avant; |
|||
* mettre les virgules à la fin des lignes de préférence; |
|||
* utiliser des guillemets simples; |
|||
* écrire en camelcase; |
|||
* utiliser 4 espaces pour l'indentation. |
|||
|
|||
## 4. Langue |
|||
|
|||
De préférence, les noms de variables, fonctions, le texte des commentaires, |
|||
les descriptions de commits doivent être écrits en *anglais.* |
@ -1,52 +0,0 @@ |
|||
# Contributing |
|||
|
|||
[Voir ceci en français](CONTRIBUTING.fr.md) |
|||
|
|||
Thank you for your interest in contributing to this repo! |
|||
All contributions (even small ones) are welcome. |
|||
In order to keep this repo consistent, please |
|||
try to follow these rules. |
|||
|
|||
## 1. Commit tags |
|||
|
|||
All commits should be tagged with emojis whenever possible |
|||
to make the commit list more readable. |
|||
|
|||
| Emoji | Commit content | |
|||
|:----------:|:--------------------- | |
|||
| :book: | Documentation updates | |
|||
| :bug: | Bug fixes | |
|||
| :ledger: | Moving files | |
|||
| :bulb: | New features | |
|||
| :lipstick: | Fixing coding style | |
|||
|
|||
## 2. Branches |
|||
|
|||
Please use a branch name that differs from `master` |
|||
when making pull requests, so that the network |
|||
history is more readable. |
|||
|
|||
For example, if you wanted to fix the issue |
|||
"improve documentation", you could have |
|||
chosen the following branch name: `improve-docs`. |
|||
|
|||
## 3. Coding style |
|||
|
|||
Javascript can be authored by following |
|||
[a](https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) |
|||
[lot](https://github.com/airbnb/javascript) |
|||
[of](https://github.com/felixge/node-style-guide) |
|||
[different](https://contribute.jquery.org/style-guide/js/) |
|||
[style guides](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style) |
|||
but we decided to be a bit soft on that. |
|||
|
|||
As a rule of thumb, use [ESLint](http://eslint.org/) to check if your code complies |
|||
with our style conventions. Here are some of the rules: |
|||
|
|||
* use the radix parameter in `parseInt()` calls; |
|||
* use the *one true brace style;* |
|||
* put one space after commas, and no space before; |
|||
* put your comma at the end of the lines; |
|||
* use simple quotes; |
|||
* use camelcase; |
|||
* use 4 spaces for indentation. |
@ -1,121 +0,0 @@ |
|||
Creative Commons Legal Code |
|||
|
|||
CC0 1.0 Universal |
|||
|
|||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE |
|||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN |
|||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS |
|||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES |
|||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS |
|||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM |
|||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED |
|||
HEREUNDER. |
|||
|
|||
Statement of Purpose |
|||
|
|||
The laws of most jurisdictions throughout the world automatically confer |
|||
exclusive Copyright and Related Rights (defined below) upon the creator |
|||
and subsequent owner(s) (each and all, an "owner") of an original work of |
|||
authorship and/or a database (each, a "Work"). |
|||
|
|||
Certain owners wish to permanently relinquish those rights to a Work for |
|||
the purpose of contributing to a commons of creative, cultural and |
|||
scientific works ("Commons") that the public can reliably and without fear |
|||
of later claims of infringement build upon, modify, incorporate in other |
|||
works, reuse and redistribute as freely as possible in any form whatsoever |
|||
and for any purposes, including without limitation commercial purposes. |
|||
These owners may contribute to the Commons to promote the ideal of a free |
|||
culture and the further production of creative, cultural and scientific |
|||
works, or to gain reputation or greater distribution for their Work in |
|||
part through the use and efforts of others. |
|||
|
|||
For these and/or other purposes and motivations, and without any |
|||
expectation of additional consideration or compensation, the person |
|||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she |
|||
is an owner of Copyright and Related Rights in the Work, voluntarily |
|||
elects to apply CC0 to the Work and publicly distribute the Work under its |
|||
terms, with knowledge of his or her Copyright and Related Rights in the |
|||
Work and the meaning and intended legal effect of CC0 on those rights. |
|||
|
|||
1. Copyright and Related Rights. A Work made available under CC0 may be |
|||
protected by copyright and related or neighboring rights ("Copyright and |
|||
Related Rights"). Copyright and Related Rights include, but are not |
|||
limited to, the following: |
|||
|
|||
i. the right to reproduce, adapt, distribute, perform, display, |
|||
communicate, and translate a Work; |
|||
ii. moral rights retained by the original author(s) and/or performer(s); |
|||
iii. publicity and privacy rights pertaining to a person's image or |
|||
likeness depicted in a Work; |
|||
iv. rights protecting against unfair competition in regards to a Work, |
|||
subject to the limitations in paragraph 4(a), below; |
|||
v. rights protecting the extraction, dissemination, use and reuse of data |
|||
in a Work; |
|||
vi. database rights (such as those arising under Directive 96/9/EC of the |
|||
European Parliament and of the Council of 11 March 1996 on the legal |
|||
protection of databases, and under any national implementation |
|||
thereof, including any amended or successor version of such |
|||
directive); and |
|||
vii. other similar, equivalent or corresponding rights throughout the |
|||
world based on applicable law or treaty, and any national |
|||
implementations thereof. |
|||
|
|||
2. Waiver. To the greatest extent permitted by, but not in contravention |
|||
of, applicable law, Affirmer hereby overtly, fully, permanently, |
|||
irrevocably and unconditionally waives, abandons, and surrenders all of |
|||
Affirmer's Copyright and Related Rights and associated claims and causes |
|||
of action, whether now known or unknown (including existing as well as |
|||
future claims and causes of action), in the Work (i) in all territories |
|||
worldwide, (ii) for the maximum duration provided by applicable law or |
|||
treaty (including future time extensions), (iii) in any current or future |
|||
medium and for any number of copies, and (iv) for any purpose whatsoever, |
|||
including without limitation commercial, advertising or promotional |
|||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each |
|||
member of the public at large and to the detriment of Affirmer's heirs and |
|||
successors, fully intending that such Waiver shall not be subject to |
|||
revocation, rescission, cancellation, termination, or any other legal or |
|||
equitable action to disrupt the quiet enjoyment of the Work by the public |
|||
as contemplated by Affirmer's express Statement of Purpose. |
|||
|
|||
3. Public License Fallback. Should any part of the Waiver for any reason |
|||
be judged legally invalid or ineffective under applicable law, then the |
|||
Waiver shall be preserved to the maximum extent permitted taking into |
|||
account Affirmer's express Statement of Purpose. In addition, to the |
|||
extent the Waiver is so judged Affirmer hereby grants to each affected |
|||
person a royalty-free, non transferable, non sublicensable, non exclusive, |
|||
irrevocable and unconditional license to exercise Affirmer's Copyright and |
|||
Related Rights in the Work (i) in all territories worldwide, (ii) for the |
|||
maximum duration provided by applicable law or treaty (including future |
|||
time extensions), (iii) in any current or future medium and for any number |
|||
of copies, and (iv) for any purpose whatsoever, including without |
|||
limitation commercial, advertising or promotional purposes (the |
|||
"License"). The License shall be deemed effective as of the date CC0 was |
|||
applied by Affirmer to the Work. Should any part of the License for any |
|||
reason be judged legally invalid or ineffective under applicable law, such |
|||
partial invalidity or ineffectiveness shall not invalidate the remainder |
|||
of the License, and in such case Affirmer hereby affirms that he or she |
|||
will not (i) exercise any of his or her remaining Copyright and Related |
|||
Rights in the Work or (ii) assert any associated claims and causes of |
|||
action with respect to the Work, in either case contrary to Affirmer's |
|||
express Statement of Purpose. |
|||
|
|||
4. Limitations and Disclaimers. |
|||
|
|||
a. No trademark or patent rights held by Affirmer are waived, abandoned, |
|||
surrendered, licensed or otherwise affected by this document. |
|||
b. Affirmer offers the Work as-is and makes no representations or |
|||
warranties of any kind concerning the Work, express, implied, |
|||
statutory or otherwise, including without limitation warranties of |
|||
title, merchantability, fitness for a particular purpose, non |
|||
infringement, or the absence of latent or other defects, accuracy, or |
|||
the present or absence of errors, whether or not discoverable, all to |
|||
the greatest extent permissible under applicable law. |
|||
c. Affirmer disclaims responsibility for clearing rights of other persons |
|||
that may apply to the Work or any use thereof, including without |
|||
limitation any person's Copyright and Related Rights in the Work. |
|||
Further, Affirmer disclaims responsibility for obtaining any necessary |
|||
consents, permissions or other rights required for any use of the |
|||
Work. |
|||
d. Affirmer understands and acknowledges that Creative Commons is not a |
|||
party to this document and has no duty or obligation with respect to |
|||
this CC0 or use of the Work. |
@ -1,19 +0,0 @@ |
|||
# Chaos |
|||
|
|||
[Looking for the english version?](README.md) |
|||
|
|||
Créer des fractales avec le jeu du chaos. |
|||
|
|||
== À FAIRE == |
|||
|
|||
## Contribuer |
|||
|
|||
Toutes les informations sont dans le |
|||
[guide du contributeur.](https://github.com/matteodelabre/chaos/blob/master/CONTRIBUTING.fr.md) |
|||
|
|||
## Licence |
|||
|
|||
Chaos ― Créer des fractales avec le jeu du chaos. |
|||
Écrit en 2013 ― 2015 par Mattéo Delabre ([bonjour@matteodelabre.me](mailto:bonjour@matteodelabre.me)). |
|||
Dans la mesure permise par la loi, l'auteur dédie mondialement tous ses droits d'auteur et droits voisins sur ce logiciel au **domaine public.** Ce logiciel est distribué sans aucune garantie. |
|||
Vous devriez avoir reçu une copie du *CC0 Domain Dedication* avec ce logiciel. Sinon, voir http://creativecommons.org/publicdomain/zero/1.0/. |
@ -1,18 +0,0 @@ |
|||
# Chaos |
|||
|
|||
[Voir ceci en français](README.fr.md) |
|||
|
|||
Creating fractals with the chaos game. |
|||
|
|||
== TODO == |
|||
|
|||
## Contributing |
|||
|
|||
Check out the [contribution guide.](https://github.com/matteodelabre/chaos/blob/master/CONTRIBUTING.md) |
|||
|
|||
## License |
|||
|
|||
Chaos ― Creating fractals with the chaos game. |
|||
Written in 2013 ― 2015 by Mattéo Delabre ([bonjour@matteodelabre.me](mailto:bonjour@matteodelabre.me)). |
|||
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the **public domain** worldwide. This software is distributed without any warranty. |
|||
You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see http://creativecommons.org/publicdomain/zero/1.0/. |
File diff suppressed because one or more lines are too long
Przed Szerokość: | Wysokość: | Rozmiar: 71 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 1.3 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 1.8 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 1.7 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 1.8 KiB |
@ -1,17 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="fr"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>Chaos game</title> |
|||
|
|||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic|Playfair+Display:400,400italic,700" rel="stylesheet"> |
|||
<link href="https://matteodelabre.me/styles/common.css" rel="stylesheet"> |
|||
<link href="https://matteodelabre.me/styles/demos.css" rel="stylesheet"> |
|||
<link href="styles/index.css" rel="stylesheet"> |
|||
</head> |
|||
<body> |
|||
<div id="react"></div> |
|||
|
|||
<script src="bundle.js"></script> |
|||
</body> |
|||
</html> |
Plik diff jest za duży
@ -1,33 +1,23 @@ |
|||
{ |
|||
"name": "chaos", |
|||
"version": "1.0.0", |
|||
"description": "Plotting fractals with the chaos game", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/matteodelabre/chaos.git" |
|||
"version": "2.0.0", |
|||
"description": "Exploring the chaos game", |
|||
"license": "CC0", |
|||
"private": true, |
|||
"devDependencies": { |
|||
"rollup": "^1.17.0", |
|||
"rollup-plugin-commonjs": "^10.0.1", |
|||
"rollup-plugin-livereload": "^1.0.1", |
|||
"rollup-plugin-node-resolve": "^5.2.0", |
|||
"rollup-plugin-svelte": "^5.1.0", |
|||
"rollup-plugin-terser": "^5.1.1", |
|||
"svelte": "^3.6.9" |
|||
}, |
|||
"scripts": { |
|||
"build": "browserify -t [ babelify --presets [ es2015 react ] ] scripts/index.js | babel --presets es2015 > bundle.js" |
|||
"build": "rollup -c", |
|||
"autobuild": "rollup -c -w" |
|||
}, |
|||
"keywords": [ |
|||
"chaos", |
|||
"fractals", |
|||
"game" |
|||
], |
|||
"author": "Mattéo Delabre", |
|||
"license": "CC0-1.0", |
|||
"bugs": { |
|||
"url": "https://github.com/matteodelabre/chaos/issues" |
|||
}, |
|||
"homepage": "https://github.com/matteodelabre/chaos#readme", |
|||
"dependencies": { |
|||
"babel-cli": "^6.3.17", |
|||
"babel-preset-es2015": "^6.3.13", |
|||
"babel-preset-react": "^6.3.13", |
|||
"babelify": "^7.2.0", |
|||
"browserify": "^12.0.1", |
|||
"react": "^0.14.3", |
|||
"react-dom": "^0.14.3", |
|||
"the-dom": "^0.1.0" |
|||
"ml-matrix": "^6.2.0" |
|||
} |
|||
} |
|||
|
@ -0,0 +1,16 @@ |
|||
body, html |
|||
{ |
|||
height: 100%; |
|||
overflow: hidden; |
|||
|
|||
margin: 0; |
|||
padding: 0; |
|||
outline: 0; |
|||
|
|||
--easing: cubic-bezier(0.4, 0.0, 0.2, 1); |
|||
} |
|||
|
|||
*, *::before, *::after |
|||
{ |
|||
box-sizing: border-box; |
|||
} |
@ -0,0 +1,17 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf8"> |
|||
<meta name="viewport" content="width=device-width"> |
|||
|
|||
<title>Chaos game</title> |
|||
|
|||
<link rel="stylesheet" href="/global.css"> |
|||
<link rel="stylesheet" href="/bundle.css"> |
|||
|
|||
<script defer src="/bundle.js"></script> |
|||
</head> |
|||
|
|||
<body> |
|||
</body> |
|||
</html> |
@ -0,0 +1,42 @@ |
|||
import svelte from 'rollup-plugin-svelte'; |
|||
import resolve from 'rollup-plugin-node-resolve'; |
|||
import commonjs from 'rollup-plugin-commonjs'; |
|||
import livereload from 'rollup-plugin-livereload'; |
|||
import { terser } from 'rollup-plugin-terser'; |
|||
|
|||
const production = !process.env.ROLLUP_WATCH; |
|||
|
|||
export default { |
|||
input: 'src/main.js', |
|||
output: { |
|||
sourcemap: true, |
|||
format: 'iife', |
|||
name: 'app', |
|||
file: 'public/bundle.js' |
|||
}, |
|||
plugins: [ |
|||
svelte({ |
|||
dev: !production, |
|||
css: css => css.write('public/bundle.css') |
|||
}), |
|||
|
|||
resolve({ |
|||
browser: true, |
|||
dedupe: importee => importee === 'svelte' |
|||
|| importee.startsWith('svelte/') |
|||
}), |
|||
|
|||
commonjs(), |
|||
|
|||
// In development mode, watch the `public` directory
|
|||
// and refresh the browser on changes
|
|||
!production && livereload('public'), |
|||
|
|||
// In production, minify
|
|||
production && terser() |
|||
], |
|||
watch: { |
|||
chokidar: false, |
|||
clearScreen: false |
|||
} |
|||
}; |
@ -1,26 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import * as React from 'react'; |
|||
import { Controls } from './controls'; |
|||
import { Fractal } from './fractal'; |
|||
import { sierpinski } from './ifs'; |
|||
|
|||
/** |
|||
* Render the app sidebar and fractal display |
|||
*/ |
|||
export class App extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
|
|||
}; |
|||
} |
|||
|
|||
render() { |
|||
return <div className="split"> |
|||
<Controls /> |
|||
<Fractal system={sierpinski} /> |
|||
</div>; |
|||
} |
|||
} |
@ -1,48 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Choose an index at random among a list of weights, |
|||
* more weighted indices have a greater proability to be chosen |
|||
* |
|||
* @param {Array} weights List of weights |
|||
* @return {number} Selected index |
|||
*/ |
|||
const chooseIndex = weights => { |
|||
const number = Math.random(); |
|||
let sum = 0, index = 0; |
|||
|
|||
while (number >= sum) { |
|||
sum += weights[index]; |
|||
index += 1; |
|||
} |
|||
|
|||
return index - 1; |
|||
}; |
|||
|
|||
/** |
|||
* Starting from `point`, generate `iterations` points |
|||
* by applying randomly-chosen transformations |
|||
* |
|||
* @param {Array} point Starting point |
|||
* @param {number} iterations Number of points to plot |
|||
* @param {Array} transforms List of available transforms |
|||
* @param {Array} weights Probability weights for each transform |
|||
* @return {Array} Generated points |
|||
*/ |
|||
export const applyChaos = (point, iterations, transforms, weights) => { |
|||
const points = []; |
|||
|
|||
if (weights === undefined) { |
|||
weights = Array.apply(null, Array(transforms.length)).map( |
|||
() => 1 / transforms.length |
|||
); |
|||
} |
|||
|
|||
while (iterations--) { |
|||
const index = chooseIndex(weights); |
|||
point = transforms[index](point); |
|||
points.push(point); |
|||
} |
|||
|
|||
return points; |
|||
}; |
@ -1,34 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import * as React from 'react'; |
|||
|
|||
/** |
|||
* Render app controls |
|||
*/ |
|||
export class Controls extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
|
|||
}; |
|||
} |
|||
|
|||
render() { |
|||
return <aside> |
|||
<header> |
|||
<a href="/"> |
|||
<img src="images/avatar.jpg" alt="Photo de Mattéo" /> |
|||
<span>Mattéo Delabre ✏️</span><br /> |
|||
Back to home |
|||
</a> |
|||
</header> |
|||
|
|||
<h1>The Chaos Game</h1> |
|||
<h3>Creating fractals with the chaos game</h3> |
|||
|
|||
Sommets <input id="vertices" type="range" min="3" max="12" step="1" defaultValue="3" /><br /> |
|||
Fraction 1/<input id="fraction" type="range" min="1" max="6" step="0.01" defaultValue="2" /> |
|||
</aside>; |
|||
} |
|||
} |
@ -1,246 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import * as React from 'react'; |
|||
import { applyChaos } from './chaos'; |
|||
|
|||
/** |
|||
* Render a canvas element that draws a fractal out of a |
|||
* given iterated function system |
|||
*/ |
|||
class Fractal extends React.Component { |
|||
constructor(props) { |
|||
super(props); |
|||
|
|||
this.state = { |
|||
zoom: 30, |
|||
dragging: false, |
|||
center: null, |
|||
points: null |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Adjust zooming level and center so that wheeling the mouse |
|||
* on given point zooms around it |
|||
* |
|||
* @param {WheelEvent} event Mouse event |
|||
* @return {null} |
|||
*/ |
|||
wheel(event) { |
|||
const zoom = this.state.zoom; |
|||
const center = this.state.center; |
|||
const height = this.ctx.canvas.height; |
|||
|
|||
const delta = event.deltaMode === 0 ? event.deltaY / 53 : event.deltaY; |
|||
const newZoom = zoom * Math.max(0, 1 - delta * .035); |
|||
|
|||
// which (unprojected) point does the mouse point on?
|
|||
const mouse = [ |
|||
(event.nativeEvent.offsetX - center[0]) / zoom, |
|||
(height - event.nativeEvent.offsetY - center[1]) / zoom |
|||
]; |
|||
|
|||
// we need to set the center so that `mouse` stays at
|
|||
// the same position on screen, i.e (vectorially):
|
|||
// mouse * newZoom + newCenter = mouse * zoom + center
|
|||
// => newCenter = mouse * zoom - mouse * newZoom + center
|
|||
this.setState({ |
|||
zoom: newZoom, |
|||
center: [ |
|||
mouse[0] * zoom - mouse[0] * newZoom + center[0], |
|||
mouse[1] * zoom - mouse[1] * newZoom + center[1] |
|||
] |
|||
}, this.draw); |
|||
|
|||
event.preventDefault(); |
|||
} |
|||
|
|||
/** |
|||
* Save the mouse coordinates when we click down, |
|||
* for use by `mouseMove()` |
|||
* |
|||
* @param {MouseEvent} event Mouse event |
|||
* @return {null} |
|||
*/ |
|||
mouseDown(event) { |
|||
this.setState({ |
|||
dragging: [ |
|||
event.nativeEvent.offsetX, |
|||
event.nativeEvent.offsetY |
|||
] |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Save the fact that the mouse was released |
|||
* |
|||
* @return {null} |
|||
*/ |
|||
mouseUp() { |
|||
this.setState({ |
|||
dragging: false |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* When moving the mouse while clicking, pan the figure |
|||
* |
|||
* @param {MouseEvent} event Mouse event |
|||
* @return {null} |
|||
*/ |
|||
mouseMove(event) { |
|||
const dragging = this.state.dragging; |
|||
const center = this.state.center; |
|||
|
|||
if (dragging !== false) { |
|||
const newMouse = [ |
|||
event.nativeEvent.offsetX, |
|||
event.nativeEvent.offsetY |
|||
]; |
|||
|
|||
const movement = [ |
|||
newMouse[0] - dragging[0], |
|||
newMouse[1] - dragging[1] |
|||
]; |
|||
|
|||
// move the center by given offset and redraw
|
|||
this.setState({ |
|||
dragging: newMouse, |
|||
center: [ |
|||
center[0] + movement[0], |
|||
center[1] - movement[1] |
|||
] |
|||
}, this.draw); |
|||
|
|||
event.preventDefault(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Redraw `points` on the canvas, scaled with given |
|||
* `zoom` and based on current `center` |
|||
* |
|||
* @return {null} |
|||
*/ |
|||
draw() { |
|||
const width = this.ctx.canvas.width; |
|||
const height = this.ctx.canvas.height; |
|||
const zoom = this.state.zoom; |
|||
const center = this.state.center; |
|||
const points = this.state.points; |
|||
|
|||
// do not plot (very) small sizes
|
|||
if (width < 1) { |
|||
return; |
|||
} |
|||
|
|||
this.ctx.clearRect(0, 0, width, height); |
|||
|
|||
// fill each point in `points` skipping the first 50 ones
|
|||
const image = this.ctx.getImageData(0, 0, width, height); |
|||
const length = points.length; |
|||
const color = [0, 0, 0]; |
|||
|
|||
for (let i = 50; i < length; i += 1) { |
|||
const x = Math.floor(points[i][0] * zoom + center[0]); |
|||
const y = height - Math.floor(points[i][1] * zoom + center[1]); |
|||
|
|||
if (x >= 0 && x < width && y >= 0 && y < height) { |
|||
const index = (y * width + x) * 4; |
|||
|
|||
image.data[index] = color[0]; |
|||
image.data[index + 1] = color[1]; |
|||
image.data[index + 2] = color[2]; |
|||
image.data[index + 3] = 255; |
|||
} |
|||
} |
|||
|
|||
this.ctx.putImageData(image, 0, 0); |
|||
} |
|||
|
|||
/** |
|||
* Get the container size |
|||
* |
|||
* @return {Array} Width and height of the container |
|||
*/ |
|||
getSize() { |
|||
return [this.container.clientWidth, this.container.clientHeight]; |
|||
} |
|||
|
|||
/** |
|||
* Update the canvas size to its parent size |
|||
* and redraw |
|||
* |
|||
* @return {null} |
|||
*/ |
|||
resize() { |
|||
[this.ctx.canvas.width, this.ctx.canvas.height] = this.getSize(); |
|||
this.draw(); |
|||
} |
|||
|
|||
/** |
|||
* Calculate points with current system |
|||
* |
|||
* @return {Array} Points to be drawn |
|||
*/ |
|||
calculate() { |
|||
return applyChaos( |
|||
[0, 0], |
|||
this.props.iterations, |
|||
...this.props.system |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Setup resize listener and make initial drawing |
|||
* when the component has been mounted |
|||
* |
|||
* @return {null} |
|||
*/ |
|||
componentDidMount() { |
|||
window.addEventListener('resize', this.resize.bind(this)); |
|||
|
|||
this.setState({ |
|||
center: this.getSize().map(x => Math.floor(x / 2)), |
|||
points: this.calculate() |
|||
}, this.resize.bind(this)); |
|||
} |
|||
|
|||
/** |
|||
* Remove the resize listener before unmounting the component |
|||
* |
|||
* @return {null} |
|||
*/ |
|||
componentWillUnmount() { |
|||
window.removeEventListener('resize', this.resize.bind(this)); |
|||
} |
|||
|
|||
/** |
|||
* Never create a new canvas |
|||
*/ |
|||
shouldComponentUpdate() { |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Create a canvas with correct listeners |
|||
*/ |
|||
render() { |
|||
return <div id="content" ref={div => this.container = div}> |
|||
<canvas |
|||
ref={canvas => this.ctx = canvas.getContext('2d')} |
|||
onWheel={this.wheel.bind(this)} |
|||
onMouseDown={this.mouseDown.bind(this)} |
|||
onMouseUp={this.mouseUp.bind(this)} |
|||
onMouseMove={this.mouseMove.bind(this)} |
|||
></canvas> |
|||
</div>; |
|||
} |
|||
} |
|||
|
|||
Fractal.defaultProps = { |
|||
iterations: 200000, |
|||
system: [[], []] |
|||
}; |
|||
|
|||
export { Fractal }; |
@ -1,41 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
const linearTransform = (a, b, c, d, e, f) => point => [ |
|||
a * point[0] + b * point[1] + e, |
|||
c * point[0] + d * point[1] + f |
|||
]; |
|||
|
|||
const polygonTransforms = (vertices, frac) => vertices.map( |
|||
vertex => linearTransform( |
|||
frac, 0, 0, frac, |
|||
vertex[0] * (frac - 1), vertex[1] * (frac - 1) |
|||
) |
|||
); |
|||
|
|||
const regularVertices = count => { |
|||
var step = 2 * Math.PI / count; |
|||
var initial = -Math.atan(Math.sin(step) / (Math.cos(step) - 1)); |
|||
var result = []; |
|||
|
|||
for (var i = 0; i < count; i += 1) { |
|||
var current = step * i + initial; |
|||
|
|||
result.push([Math.cos(current) * 10, Math.sin(current) * 10]); |
|||
} |
|||
|
|||
return result; |
|||
}; |
|||
|
|||
export const barnsley = [[ |
|||
linearTransform(0, 0, 0, 0.16, 0, 0), |
|||
linearTransform(.85, .04, -.04, .85, 0, 1.6), |
|||
linearTransform(.20, -.26, .23, .22, 0, 1.6), |
|||
linearTransform(-.15, .28, .26, .24, 0, .44) |
|||
], [ |
|||
.01, .85, .07, .07 |
|||
]]; |
|||
|
|||
export const sierpinski = [ |
|||
polygonTransforms(regularVertices(3), 1 / 2), |
|||
[1 / 3, 1 / 3, 1 / 3] |
|||
]; |
@ -1,10 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
import { App } from './app'; |
|||
import * as React from 'react'; // eslint-disable-line no-unused-vars
|
|||
import { render } from 'react-dom'; |
|||
|
|||
render( |
|||
<App />, |
|||
document.querySelector('#react') |
|||
); |
@ -0,0 +1,224 @@ |
|||
<script> |
|||
import { onMount } from 'svelte'; |
|||
import { writable, derived } from 'svelte/store'; |
|||
import { Matrix } from 'ml-matrix'; |
|||
|
|||
import Chaos from './Chaos.svelte'; |
|||
import Point from './Point.svelte'; |
|||
|
|||
// Page dimensions |
|||
let pageWidth; |
|||
let pageHeight; |
|||
|
|||
// Controls dimensions |
|||
const controlsWidth = 200; |
|||
|
|||
// Render dimensions |
|||
$: renderWidth = pageWidth - controlsWidth; |
|||
$: renderHeight = pageHeight; |
|||
|
|||
// Number of points |
|||
let count = 3; |
|||
|
|||
// Whether controls are shown |
|||
let controls = true; |
|||
|
|||
// Fraction |
|||
const ratios = writable([]); |
|||
|
|||
// Bounding points of the fractal |
|||
const points = writable([]); |
|||
|
|||
// Center of the fractal |
|||
const center = derived(points, ($points, set) => |
|||
{ |
|||
set(Matrix.div( |
|||
$points.reduce( |
|||
(prev, point) => Matrix.add(prev, point), |
|||
Matrix.zeros(3, 1) |
|||
), |
|||
$points.length |
|||
)); |
|||
}); |
|||
|
|||
// Derived IFS |
|||
const ifs = derived([ratios, points], ([$ratios, $points], set) => |
|||
{ |
|||
set($points.map((point, index) => [ |
|||
1, |
|||
new Matrix([ |
|||
[$ratios[index], 0, point.get(0, 0) * (1 - $ratios[index])], |
|||
[0, $ratios[index], point.get(1, 0) * (1 - $ratios[index])], |
|||
[0, 0, 1] |
|||
]) |
|||
])) |
|||
}); |
|||
|
|||
const myifs = [ |
|||
[1, new Matrix([ |
|||
[0, 0, 0], |
|||
[0, 0.16, 0], |
|||
[0, 0, 1] |
|||
])], |
|||
[85, new Matrix([ |
|||
[0.85, 0.04, 0], |
|||
[-0.04, 0.85, 160], |
|||
[0, 0, 1] |
|||
])], |
|||
[7, new Matrix([ |
|||
[0.20, -0.26, 0], |
|||
[0.23, 0.22, 160], |
|||
[0, 0, 1] |
|||
])], |
|||
[7, new Matrix([ |
|||
[-0.15, 0.28, 0], |
|||
[0.26, 0.24, 44], |
|||
[0, 0, 1] |
|||
])] |
|||
]; |
|||
|
|||
for (let f of myifs) |
|||
{ |
|||
/* f[1] = new Matrix([ */ |
|||
/* [1, 0, 100], */ |
|||
/* [0, 1, 100], */ |
|||
/* [0, 0, 1] */ |
|||
/* ]).mmul(f[1]); */ |
|||
} |
|||
|
|||
console.log(myifs); |
|||
|
|||
$: { |
|||
// Generate a regular polygon with the given number of vertices |
|||
// and half ratio for each vertex |
|||
const pageCenter = [renderWidth / 2, renderHeight / 2]; |
|||
const radius = Math.min(renderWidth, renderHeight) * 0.4; |
|||
|
|||
points.set( |
|||
Array.from({length: count}).map( |
|||
(_, index) => index * 2 * Math.PI / count - Math.PI / 2 |
|||
).map(angle => Matrix.columnVector([ |
|||
pageCenter[0] + Math.cos(angle) * radius, |
|||
pageCenter[1] + Math.sin(angle) * radius, |
|||
1 |
|||
])) |
|||
); |
|||
|
|||
ratios.set( |
|||
Array.from({length: count}).fill(0.5) |
|||
); |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.app |
|||
{ |
|||
display: flex; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.controls |
|||
{ |
|||
flex: 1; |
|||
} |
|||
|
|||
.render |
|||
{ |
|||
flex: 1; |
|||
position: relative; |
|||
} |
|||
|
|||
.render .overlay |
|||
{ |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
} |
|||
</style> |
|||
|
|||
<div class="app" bind:clientWidth={pageWidth} bind:clientHeight={pageHeight}> |
|||
<aside class="controls" style="width: {controlsWidth}px"> |
|||
<p> |
|||
<button on:click={() => ++count}>Add point</button> |
|||
<button on:click={() => --count}>Remove point</button> |
|||
</p> |
|||
|
|||
<p> |
|||
{#if controls} |
|||
Controls visible. |
|||
<button on:click={() => controls = false}>Hide</button> |
|||
{:else} |
|||
Controls hidden. |
|||
<button on:click={() => controls = true}>Show</button> |
|||
{/if} |
|||
</p> |
|||
</aside> |
|||
|
|||
<main class="render" style="width: {renderWidth}px"> |
|||
<Chaos |
|||
width={renderWidth} height={renderHeight} |
|||
start={$center} ifs={$ifs} |
|||
/> |
|||
|
|||
{#if controls} |
|||
<svg |
|||
class="overlay" |
|||
width={renderWidth} height={renderHeight} |
|||
viewBox="0 0 {renderWidth} {renderHeight}" |
|||
> |
|||
{#each $points as point} |
|||
<line |
|||
stroke="#333" |
|||
x1={$center.get(0, 0)} |
|||
y1={$center.get(1, 0)} |
|||
x2={point.get(0, 0)} |
|||
y2={point.get(1, 0)} |
|||
/> |
|||
{/each} |
|||
</svg> |
|||
|
|||
{#each $points as point, index} |
|||
<Point |
|||
x={point.get(0, 0)} |
|||
y={point.get(1, 0)} |
|||
on:move={evt => points.set([ |
|||
...$points.slice(0, index), |
|||
Matrix.columnVector([evt.detail.x, evt.detail.y, 1]), |
|||
...$points.slice(index + 1) |
|||
])} |
|||
/> |
|||
{/each} |
|||
|
|||
{#each $ifs as current, index} |
|||
<Point |
|||
x={current[1].mmul($center).get(0, 0)} |
|||
y={current[1].mmul($center).get(1, 0)} |
|||
on:move={evt => { |
|||
const point = Matrix.columnVector([evt.detail.x, evt.detail.y, 1]); |
|||
|
|||
const vec1 = Matrix.sub($points[index], $center); |
|||
const vec2 = Matrix.sub(point, $center); |
|||
|
|||
const nextRatio = 1 - vec1.dot(vec2) / vec1.dot(vec1); |
|||
|
|||
if (evt.detail.shift) |
|||
{ |
|||
ratios.set( |
|||
Array.from({length: $ratios.length}).fill(nextRatio) |
|||
); |
|||
} |
|||
else |
|||
{ |
|||
ratios.set([ |
|||
...$ratios.slice(0, index), |
|||
nextRatio, |
|||
...$ratios.slice(index + 1) |
|||
]); |
|||
} |
|||
}} |
|||
/> |
|||
{/each} |
|||
{/if} |
|||
</main> |
|||
</div> |
@ -0,0 +1,147 @@ |
|||
<!-- |
|||
Simulate a chaos game on an iterated function set (IFS) |
|||
and plot the result on a canvas of given dimensions. |
|||
--> |
|||
|
|||
<script> |
|||
import { onMount, onDestroy, tick, afterUpdate } from 'svelte'; |
|||
import { Matrix } from 'ml-matrix'; |
|||
import { choose } from './util.js'; |
|||
|
|||
/** Width of the canvas on which the chaos figure is rendered. */ |
|||
export let width = 500; |
|||
|
|||
/** Height of the canvas on which the chaos figure is rendered. */ |
|||
export let height = 500; |
|||
|
|||
/** Total number of iterations to perform. */ |
|||
export let totalIterations = 100000; |
|||
|
|||
/** Number of iterations to perform in one frame. */ |
|||
export let iterationsPerFrame = 1000; |
|||
|
|||
/** |
|||
* Iterated function set to use for rendering the figure as a list of |
|||
* pairs, each containing the weight of the function and the function |
|||
* as a 3×3 matrix. |
|||
*/ |
|||
export let ifs = [[1, Matrix.identity(3)]]; |
|||
|
|||
/** Initial point for the iteration, in homogeneous coordinates. */ |
|||
export let start = Matrix.columnVector([0, 0, 1]); |
|||
|
|||
// Canvas and drawing context on which the figure is rendered |
|||
let canvas; |
|||
let ctx; |
|||
|
|||
// Handle to the next scheduled frame |
|||
let nextFrame = null; |
|||
|
|||
/** |
|||
* Perform random chaos iteration on the given point, leaving traces |
|||
* behind as the iteration goes. |
|||
* |
|||
* @param point Starting point for the iteration. |
|||
* @param ifs Iterated function set to use. |
|||
* @param iterations Number of iterations to perform. |
|||
*/ |
|||
const randomIterate = (point, ifs, iterations) => |
|||
{ |
|||
// Only perform a limited amount of iterations and |
|||
// defer the remaining ones to the next frame |
|||
for (let i = 0; i < iterationsPerFrame; ++i) |
|||
{ |
|||
const [_, matrix] = choose(ifs); |
|||
point = matrix.mmul(point); |
|||
|
|||
const x = Math.floor(point.get(0, 0)); |
|||
const y = Math.floor(point.get(1, 0)); |
|||
|
|||
if (x >= 0 && x < width && y >= 0 && y < height) |
|||
{ |
|||
ctx.fillRect(x, y, 1, 1); |
|||
} |
|||
} |
|||
|
|||
if (iterations > iterationsPerFrame) |
|||
{ |
|||
nextFrame = window.requestAnimationFrame(() => randomIterate( |
|||
point, ifs, |
|||
iterations - iterationsPerFrame |
|||
)); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* Perform deterministic chaos iteration on each of the given points, |
|||
* leaving traces behind as the iteration goes. |
|||
* |
|||
* @param points Seed points, modified in-place. |
|||
* @param index Index from which to iterate in the points array. |
|||
* @param ifs Iterated function set to use. |
|||
* @param iterations Number of iterations to perform. |
|||
*/ |
|||
const deterministicIterate = (points, index, ifs, iterations) => |
|||
{ |
|||
let i = index; |
|||
|
|||
for ( |
|||
let frameIterations = 0; |
|||
frameIterations < iterationsPerFrame; |
|||
frameIterations += ifs.length, |
|||
i = (i + ifs.length) % points.length |
|||
) |
|||
{ |
|||
points.splice( |
|||
i, 1, |
|||
...ifs.map(([_, matrix]) => |
|||
{ |
|||
const nextPoint = matrix.mmul(points[i]); |
|||
const x = Math.floor(nextPoint.get(0, 0)); |
|||
const y = Math.floor(nextPoint.get(1, 0)); |
|||
|
|||
if (x >= 0 && x < width && y >= 0 && y < height) |
|||
{ |
|||
ctx.fillRect(x, y, 1, 1); |
|||
} |
|||
|
|||
return nextPoint; |
|||
}) |
|||
); |
|||
} |
|||
|
|||
if (iterations > iterationsPerFrame) |
|||
{ |
|||
nextFrame = window.requestAnimationFrame(() => deterministicIterate( |
|||
points, i, ifs, |
|||
iterations - iterationsPerFrame |
|||
)); |
|||
} |
|||
}; |
|||
|
|||
/** Interrupt the iteration process. */ |
|||
const stopLoop = () => |
|||
{ |
|||
window.cancelAnimationFrame(nextFrame); |
|||
}; |
|||
|
|||
$: { |
|||
stopLoop(); |
|||
|
|||
if (ctx && ifs.length) |
|||
{ |
|||
ctx.clearRect(0, 0, width, height); |
|||
ctx.fillStyle = 'black'; |
|||
deterministicIterate([start], 0, ifs, totalIterations); |
|||
} |
|||
} |
|||
|
|||
onMount(() => ctx = canvas.getContext('2d')); |
|||
onDestroy(() => stopLoop()); |
|||
</script> |
|||
|
|||
<canvas |
|||
bind:this={canvas} |
|||
draggable=false |
|||
width={width} height={height} |
|||
/> |
@ -0,0 +1,119 @@ |
|||
<!-- |
|||
Represent a point on the screen that can optionally be moved by the user. |
|||
--> |
|||
|
|||
<script> |
|||
import { onMount, onDestroy, createEventDispatcher } from 'svelte'; |
|||
|
|||
/** Horizontal position of the point. */ |
|||
export let x = 0; |
|||
|
|||
/** Vertical position of the point. */ |
|||
export let y = 0; |
|||
|
|||
/** Radius of the circle that represents the point. */ |
|||
export let size = 15; |
|||
|
|||
/** Whether the user is allowed to move the point. */ |
|||
export let movable = true; |
|||
|
|||
// Reference to the <div> element that represents the point |
|||
let element = null; |
|||
|
|||
// Bounds of the parent node, computed only once when the user |
|||
// starts dragging |
|||
let bounds; |
|||
|
|||
// Whether the user is currently dragging the point |
|||
let dragging = false; |
|||
|
|||
let dispatch = createEventDispatcher(); |
|||
|
|||
const start = evt => |
|||
{ |
|||
if (!movable) return; |
|||
|
|||
bounds = element.parentNode.getBoundingClientRect(); |
|||
dragging = true; |
|||
|
|||
window.addEventListener('mousemove', move); |
|||
window.addEventListener('touchmove', move); |
|||
window.addEventListener('mouseup', end); |
|||
window.addEventListener('touchend', end); |
|||
}; |
|||
|
|||
const clamp = (value, min, max) => Math.max(min, Math.min(max, value)); |
|||
|
|||
const move = evt => |
|||
{ |
|||
const [userX, userY] = evt.changedTouches |
|||
? [evt.changedTouches[0].clientX, evt.changedTouches[0].clientY] |
|||
: [evt.clientX, evt.clientY]; |
|||
|
|||
dispatch('move', { |
|||
x: clamp(userX - bounds.left, 0, bounds.width), |
|||
y: clamp(userY - bounds.top, 0, bounds.height), |
|||
shift: evt.shiftKey |
|||
}); |
|||
}; |
|||
|
|||
const end = evt => |
|||
{ |
|||
dragging = false; |
|||
window.removeEventListener('mousemove', move); |
|||
window.removeEventListener('touchmove', move); |
|||
window.removeEventListener('mouseup', end); |
|||
window.removeEventListener('touchend', end); |
|||
}; |
|||
|
|||
onMount(() => |
|||
{ |
|||
element.addEventListener('mousedown', start); |
|||
element.addEventListener('touchstart', start); |
|||
}); |
|||
|
|||
onDestroy(() => |
|||
{ |
|||
element.removeEventListener('mousedown', start); |
|||
element.removeEventListener('touchstart', start); |
|||
window.removeEventListener('mousemove', move); |
|||
window.removeEventListener('touchmove', move); |
|||
window.removeEventListener('mouseup', end); |
|||
window.removeEventListener('touchend', end); |
|||
}); |
|||
</script> |
|||
|
|||
<style> |
|||
div |
|||
{ |
|||
display: block; |
|||
position: absolute; |
|||
border-radius: 100%; |
|||
background: #333; |
|||
|
|||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1); |
|||
transition: |
|||
box-shadow .15s var(--easing), |
|||
transform .15s var(--easing), |
|||
opacity .15s var(--easing); |
|||
} |
|||
|
|||
div.movable |
|||
{ |
|||
cursor: pointer; |
|||
} |
|||
|
|||
div.movable.dragging |
|||
{ |
|||
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.2); |
|||
transform: scale(1.5); |
|||
} |
|||
</style> |
|||
|
|||
<div |
|||
style="width: {size}px; height: {size}px; left: {x - size / 2}px; top: {y - size / 2}px;" |
|||
class:dragging |
|||
class:movable |
|||
draggable="false" |
|||
bind:this={element} |
|||
/> |
@ -0,0 +1,4 @@ |
|||
import App from './App.svelte'; |
|||
|
|||
const app = new App({target: document.body}); |
|||
export default app; |
@ -0,0 +1,27 @@ |
|||
/** |
|||
* Choose a pair at random among a list of weighted pairs. |
|||
* |
|||
* @param pairs List of weighted pairs, with the weight coming first. |
|||
* @return Selected pair, or null if there is nothing to choose from. |
|||
*/ |
|||
export const choose = pairs => |
|||
{ |
|||
const total = pairs.reduce((prev, [weight, _]) => prev + weight, 0); |
|||
|
|||
if (total === 0) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
const value = Math.random() * total; |
|||
let sum = 0; |
|||
let index = 0; |
|||
|
|||
while (value >= sum) |
|||
{ |
|||
sum += pairs[index][0]; |
|||
index += 1; |
|||
} |
|||
|
|||
return pairs[index - 1]; |
|||
}; |
@ -1,11 +0,0 @@ |
|||
/** |
|||
* Styles for chaos game |
|||
*/ |
|||
|
|||
#react { |
|||
height: 100%; |
|||
} |
|||
|
|||
#content { |
|||
overflow: hidden; |
|||
} |
Ładowanie…
Reference in new issue