├── .gitignore
├── LICENSE.md
├── README.md
├── assets
├── demo.gif
└── screen.png
├── config
├── env.js
├── paths.js
├── polyfills.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── package.json
├── public
└── index.html
├── scripts
├── build.js
└── start.js
├── src
├── actions
│ └── index.js
├── code-transform
│ └── index.js
├── components
│ ├── code-preview
│ │ ├── index.css
│ │ └── index.js
│ ├── header
│ │ ├── index.css
│ │ └── index.js
│ ├── info
│ │ ├── index.css
│ │ └── index.js
│ ├── intro
│ │ ├── index.css
│ │ └── index.js
│ ├── modal
│ │ ├── index.css
│ │ └── index.js
│ ├── population
│ │ ├── index.css
│ │ └── index.js
│ └── renderer
│ │ ├── index.css
│ │ └── index.js
├── constants.js
├── genetic
│ ├── evolution.worker.js
│ ├── genotype.js
│ └── population.js
├── index.css
├── index.js
├── reducers
│ └── index.js
└── utils.js
├── tests
├── code-transform-electron-tests
│ ├── README.md
│ ├── index.js
│ └── test-code-transform-electron.sh
└── code-transform-test
│ ├── color-smoke.js
│ ├── complex-control.js
│ ├── detection-01.js
│ ├── detection-02.js
│ ├── distortion-tests.js
│ ├── flow-field.js
│ ├── lorenz.js
│ ├── neon-waves.js
│ ├── p5-l-system.js
│ ├── p5-noise-wave.js
│ ├── p5-particle-sys.js
│ ├── p5-sine-wave.js
│ ├── shiffman-supershapes.js
│ └── simple-drawing.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 |
4 | # Node.js
5 | node_modules/
6 |
7 | # VIM session
8 | session.vim
9 |
10 | # logs
11 | *.log
12 |
13 | # production build
14 | build/
15 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Szymon Kaliski
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Parametrium
2 |
3 | ## Interactive parameter space explorer for P5.js
4 |
5 |

6 |
7 | Live: [http://szymonkaliski.github.io/parametrium](http://szymonkaliski.github.io/parametrium)
8 |
9 | Made with [recast](https://github.com/benjamn/recast), [react](https://facebook.github.io/react/), [redux](https://github.com/reactjs/redux) and [immutable](https://facebook.github.io/immutable-js/).
10 |
11 | Features AST-walker with number detection, and evolutionary algorithm adapted from [WallGen](http://szymonkaliski.github.io/wallgen).
12 |
13 | ## Run
14 |
15 | 1. clone this repo
16 | 2. `yarn` or `npm install`
17 | 3. `yarn run start` or `npm start`
18 |
19 | ## Build
20 |
21 | 1. `yarn run build` or `npm run build`
22 |
23 |
--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/szymonkaliski/parametrium/a37db6c0c15ee6b7d6e48af1c67daa28369ed098/assets/demo.gif
--------------------------------------------------------------------------------
/assets/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/szymonkaliski/parametrium/a37db6c0c15ee6b7d6e48af1c67daa28369ed098/assets/screen.png
--------------------------------------------------------------------------------
/config/env.js:
--------------------------------------------------------------------------------
1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
2 | // injected into the application via DefinePlugin in Webpack configuration.
3 |
4 | var REACT_APP = /^REACT_APP_/i;
5 |
6 | function getClientEnvironment(publicUrl) {
7 | var processEnv = Object
8 | .keys(process.env)
9 | .filter(key => REACT_APP.test(key))
10 | .reduce((env, key) => {
11 | env[key] = JSON.stringify(process.env[key]);
12 | return env;
13 | }, {
14 | // Useful for determining whether we’re running in production mode.
15 | // Most importantly, it switches React into the correct mode.
16 | 'NODE_ENV': JSON.stringify(
17 | process.env.NODE_ENV || 'development'
18 | ),
19 | // Useful for resolving the correct path to static assets in `public`.
20 | // For example,
.
21 | // This should only be used as an escape hatch. Normally you would put
22 | // images into the `src` and `import` them in code to get their paths.
23 | 'PUBLIC_URL': JSON.stringify(publicUrl)
24 | });
25 | return {'process.env': processEnv};
26 | }
27 |
28 | module.exports = getClientEnvironment;
29 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var fs = require('fs');
3 |
4 | // Make sure any symlinks in the project folder are resolved:
5 | // https://github.com/facebookincubator/create-react-app/issues/637
6 | var appDirectory = fs.realpathSync(process.cwd());
7 | function resolveApp(relativePath) {
8 | return path.resolve(appDirectory, relativePath);
9 | }
10 |
11 | // We support resolving modules according to `NODE_PATH`.
12 | // This lets you use absolute paths in imports inside large monorepos:
13 | // https://github.com/facebookincubator/create-react-app/issues/253.
14 |
15 | // It works similar to `NODE_PATH` in Node itself:
16 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
17 |
18 | // We will export `nodePaths` as an array of absolute paths.
19 | // It will then be used by Webpack configs.
20 | // Jest doesn’t need this because it already handles `NODE_PATH` out of the box.
21 |
22 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
23 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
24 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
25 |
26 | var nodePaths = (process.env.NODE_PATH || '')
27 | .split(process.platform === 'win32' ? ';' : ':')
28 | .filter(Boolean)
29 | .filter(folder => !path.isAbsolute(folder))
30 | .map(resolveApp);
31 |
32 | // config after eject: we're in ./config/
33 | module.exports = {
34 | appBuild: resolveApp('build'),
35 | appPublic: resolveApp('public'),
36 | appHtml: resolveApp('public/index.html'),
37 | appIndexJs: resolveApp('src/index.js'),
38 | appPackageJson: resolveApp('package.json'),
39 | appSrc: resolveApp('src'),
40 | yarnLockFile: resolveApp('yarn.lock'),
41 | testsSetup: resolveApp('src/setupTests.js'),
42 | appNodeModules: resolveApp('node_modules'),
43 | ownNodeModules: resolveApp('node_modules'),
44 | nodePaths: nodePaths
45 | };
46 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | if (typeof Promise === 'undefined') {
2 | // Rejection tracking prevents a common issue where React gets into an
3 | // inconsistent state due to an error, but it gets swallowed by a Promise,
4 | // and the user has no idea what causes React's erratic future behavior.
5 | require('promise/lib/rejection-tracking').enable();
6 | window.Promise = require('promise/lib/es6-extensions.js');
7 | }
8 |
9 | // fetch() polyfill for making API calls.
10 | require('whatwg-fetch');
11 |
12 | // Object.assign() is commonly used with React.
13 | // It will use the native implementation if it's present and isn't buggy.
14 | Object.assign = require('object-assign');
15 |
--------------------------------------------------------------------------------
/config/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var autoprefixer = require('autoprefixer');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
5 | var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
6 | var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
7 | var getClientEnvironment = require('./env');
8 | var paths = require('./paths');
9 |
10 |
11 |
12 | // Webpack uses `publicPath` to determine where the app is being served from.
13 | // In development, we always serve from the root. This makes config easier.
14 | var publicPath = '/';
15 | // `publicUrl` is just like `publicPath`, but we will provide it to our app
16 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
17 | // Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
18 | var publicUrl = '';
19 | // Get environment variables to inject into our app.
20 | var env = getClientEnvironment(publicUrl);
21 |
22 | // This is the development configuration.
23 | // It is focused on developer experience and fast rebuilds.
24 | // The production configuration is different and lives in a separate file.
25 | module.exports = {
26 | // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
27 | // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
28 | devtool: 'cheap-module-source-map',
29 | // These are the "entry points" to our application.
30 | // This means they will be the "root" imports that are included in JS bundle.
31 | // The first two entry points enable "hot" CSS and auto-refreshes for JS.
32 | entry: [
33 | // Include an alternative client for WebpackDevServer. A client's job is to
34 | // connect to WebpackDevServer by a socket and get notified about changes.
35 | // When you save a file, the client will either apply hot updates (in case
36 | // of CSS changes), or refresh the page (in case of JS changes). When you
37 | // make a syntax error, this client will display a syntax error overlay.
38 | // Note: instead of the default WebpackDevServer client, we use a custom one
39 | // to bring better experience for Create React App users. You can replace
40 | // the line below with these two lines if you prefer the stock client:
41 | // require.resolve('webpack-dev-server/client') + '?/',
42 | // require.resolve('webpack/hot/dev-server'),
43 | require.resolve('react-dev-utils/webpackHotDevClient'),
44 | // We ship a few polyfills by default:
45 | require.resolve('./polyfills'),
46 | // Finally, this is your app's code:
47 | paths.appIndexJs
48 | // We include the app code last so that if there is a runtime error during
49 | // initialization, it doesn't blow up the WebpackDevServer client, and
50 | // changing JS code would still trigger a refresh.
51 | ],
52 | output: {
53 | // Next line is not used in dev but WebpackDevServer crashes without it:
54 | path: paths.appBuild,
55 | // Add /* filename */ comments to generated require()s in the output.
56 | pathinfo: true,
57 | // This does not produce a real file. It's just the virtual path that is
58 | // served by WebpackDevServer in development. This is the JS bundle
59 | // containing code from all our entry points, and the Webpack runtime.
60 | filename: 'static/js/bundle.js',
61 | // This is the URL that app is served from. We use "/" in development.
62 | publicPath: publicPath
63 | },
64 | resolve: {
65 | // This allows you to set a fallback for where Webpack should look for modules.
66 | // We read `NODE_PATH` environment variable in `paths.js` and pass paths here.
67 | // We use `fallback` instead of `root` because we want `node_modules` to "win"
68 | // if there any conflicts. This matches Node resolution mechanism.
69 | // https://github.com/facebookincubator/create-react-app/issues/253
70 | fallback: paths.nodePaths,
71 | // These are the reasonable defaults supported by the Node ecosystem.
72 | // We also include JSX as a common component filename extension to support
73 | // some tools, although we do not recommend using it, see:
74 | // https://github.com/facebookincubator/create-react-app/issues/290
75 | extensions: ['.js', '.json', '.jsx', ''],
76 | alias: {
77 | // Support React Native Web
78 | // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
79 | 'react-native': 'react-native-web'
80 | }
81 | },
82 |
83 | module: {
84 | // First, run the linter.
85 | // It's important to do this before Babel processes the JS.
86 | preLoaders: [
87 | {
88 | test: /\.(js|jsx)$/,
89 | loader: 'eslint',
90 | include: paths.appSrc,
91 | }
92 | ],
93 | loaders: [
94 | // Default loader: load all assets that are not handled
95 | // by other loaders with the url loader.
96 | // Note: This list needs to be updated with every change of extensions
97 | // the other loaders match.
98 | // E.g., when adding a loader for a new supported file extension,
99 | // we need to add the supported extension to this loader too.
100 | // Add one new line in `exclude` for each loader.
101 | //
102 | // "file" loader makes sure those assets get served by WebpackDevServer.
103 | // When you `import` an asset, you get its (virtual) filename.
104 | // In production, they would get copied to the `build` folder.
105 | // "url" loader works like "file" loader except that it embeds assets
106 | // smaller than specified limit in bytes as data URLs to avoid requests.
107 | // A missing `test` is equivalent to a match.
108 | {
109 | exclude: [
110 | /\.html$/,
111 | /\.(js|jsx)$/,
112 | /\.css$/,
113 | /\.json$/,
114 | /\.svg$/
115 | ],
116 | loader: 'url',
117 | query: {
118 | limit: 10000,
119 | name: 'static/media/[name].[hash:8].[ext]'
120 | }
121 | },
122 | // Process JS with Babel.
123 | {
124 | test: /\.(js|jsx)$/,
125 | include: paths.appSrc,
126 | loader: 'babel',
127 | query: {
128 | // This is a feature of `babel-loader` for webpack (not Babel itself).
129 | // It enables caching results in ./node_modules/.cache/babel-loader/
130 | // directory for faster rebuilds.
131 | cacheDirectory: true
132 | }
133 | },
134 | // Process webworker with Babel.
135 | {
136 | test: /\.worker\.js$/,
137 | loader: 'worker!babel?presets[]=es2015'
138 | },
139 | // "postcss" loader applies autoprefixer to our CSS.
140 | // "css" loader resolves paths in CSS and adds assets as dependencies.
141 | // "style" loader turns CSS into JS modules that inject
13 |
14 |
15 |
16 | `;
19 | };
20 |
21 | class Renderer extends Component {
22 | constructor() {
23 | super();
24 |
25 | autobind(this);
26 | }
27 |
28 | componentDidUpdate(prevProps) {
29 | if (prevProps.width !== this.props.width || prevProps.height !== this.props.height) {
30 | // reload ref iframe srcdoc to get new window size in the code (if it uses window.innerWidth/Height to set canvas)
31 | if (prevProps.code === this.props.code) {
32 | this.refIframe.srcdoc = generateHTML(this.props.code);
33 | }
34 | }
35 | }
36 |
37 | onRef(ref) {
38 | this.refIframe = ref;
39 | }
40 |
41 | render() {
42 | const { width, height, code } = this.props;
43 |
44 | if (!code) {
45 | return null;
46 | }
47 |
48 | return (
49 |
57 | );
58 | }
59 | }
60 |
61 | export default Renderer;
62 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const HISTORY_SIZE = 3;
2 | export const DISPLAY_PER_PAGE = 9;
3 |
4 | export const MUTATION_CHANCE = 0.005;
5 | export const POPULATION_SIZE = DISPLAY_PER_PAGE * 100;
6 |
7 | export const SPREADS = {
8 | CONSTANT: 0.25,
9 | COLOR: 0.25,
10 | CONTROL: 0.25
11 | };
12 |
13 | export const LITERAL_TYPES = [
14 | 'CONSTANT', // just some value
15 | 'COLOR', // from COLOR_CALLESS
16 | 'CONTROL' // for & while
17 | ].reduce((acc, key) => ({ ...acc, [key]: key }), {});
18 |
19 | export const EXAMPLES = [
20 | {
21 | name: 'recursion',
22 | url: 'https://p5js.org/examples/structure-recursion.html',
23 | code: `
24 | function setup() {
25 | createCanvas(720, 400);
26 | noStroke();
27 | noLoop();
28 | }
29 |
30 | function draw() {
31 | drawCircle(width/2, 280, 6);
32 | }
33 |
34 | function drawCircle(x, radius, level) {
35 | var tt = 126 * level/4.0;
36 | fill(tt);
37 | ellipse(x, height/2, radius*2, radius*2);
38 | if(level > 1) {
39 | level = level - 1;
40 | drawCircle(x - radius/2, radius/2, level);
41 | drawCircle(x + radius/2, radius/2, level);
42 | }
43 | }`
44 | },
45 | {
46 | name: 'primitives',
47 | url: 'https://p5js.org/examples/form-shape-primitives.html',
48 | code: `
49 | function setup() {
50 | // Sets the screen to be 720 pixels wide and 400 pixels high
51 | createCanvas(720, 400);
52 | background(0);
53 | noStroke();
54 |
55 | fill(204);
56 | triangle(18, 18, 18, 360, 81, 360);
57 |
58 | fill(102);
59 | rect(81, 81, 63, 63);
60 |
61 | fill(204);
62 | quad(189, 18, 216, 18, 216, 360, 144, 360);
63 |
64 | fill(255);
65 | ellipse(252, 144, 72, 72);
66 |
67 | fill(204);
68 | triangle(288, 18, 351, 360, 288, 360);
69 |
70 | fill(255);
71 | arc(479, 300, 280, 280, PI, TWO_PI);
72 | }`
73 | },
74 | {
75 | name: 'particle system',
76 | url: 'https://p5js.org/examples/simulate-particle-system.html',
77 | code: `
78 | var system;
79 |
80 | function setup() {
81 | createCanvas(720, 400);
82 | system = new ParticleSystem(createVector(width/2, 50));
83 | }
84 |
85 | function draw() {
86 | background(51);
87 | system.addParticle();
88 | system.run();
89 | }
90 |
91 | // A simple Particle class
92 | var Particle = function(position) {
93 | this.acceleration = createVector(0, 0.05);
94 | this.velocity = createVector(random(-1, 1), random(-1, 0));
95 | this.position = position.copy();
96 | this.lifespan = 255.0;
97 | };
98 |
99 | Particle.prototype.run = function() {
100 | this.update();
101 | this.display();
102 | };
103 |
104 | // Method to update position
105 | Particle.prototype.update = function(){
106 | this.velocity.add(this.acceleration);
107 | this.position.add(this.velocity);
108 | this.lifespan -= 2;
109 | };
110 |
111 | // Method to display
112 | Particle.prototype.display = function() {
113 | stroke(200, this.lifespan);
114 | strokeWeight(2);
115 | fill(127, this.lifespan);
116 | ellipse(this.position.x, this.position.y, 12, 12);
117 | };
118 |
119 | // Is the particle still useful?
120 | Particle.prototype.isDead = function(){
121 | if (this.lifespan < 0) {
122 | return true;
123 | } else {
124 | return false;
125 | }
126 | };
127 |
128 | var ParticleSystem = function(position) {
129 | this.origin = position.copy();
130 | this.particles = [];
131 | };
132 |
133 | ParticleSystem.prototype.addParticle = function() {
134 | this.particles.push(new Particle(this.origin));
135 | };
136 |
137 | ParticleSystem.prototype.run = function() {
138 | for (var i = this.particles.length-1; i >= 0; i--) {
139 | var p = this.particles[i];
140 | p.run();
141 | if (p.isDead()) {
142 | this.particles.splice(i, 1);
143 | }
144 | }
145 | };`
146 | },
147 | {
148 | name: 'spirograph',
149 | url: 'https://p5js.org/examples/simulate-spirograph.html',
150 | code: `
151 | var NUMSINES = 20; // how many of these things can we do at once?
152 | var sines = new Array(NUMSINES); // an array to hold all the current angles
153 | var rad; // an initial radius value for the central sine
154 | var i; // a counter variable
155 |
156 | // play with these to get a sense of what's going on:
157 | var fund = 0.005; // the speed of the central sine
158 | var ratio = 1; // what multiplier for speed is each additional sine?
159 | var alpha = 50; // how opaque is the tracing system
160 |
161 | var trace = false; // are we tracing?
162 |
163 | function setup() {
164 | createCanvas(710, 400);
165 |
166 | rad = height/4; // compute radius for central circle
167 | background(204); // clear the screen
168 |
169 | for (var i = 0; i thestring.length-1) whereinstring = 0;
259 |
260 | }
261 |
262 | // interpret an L-system
263 | function lindenmayer(s) {
264 | var outputstring = ''; // start a blank output string
265 |
266 | // iterate through 'therules' looking for symbol matches:
267 | for (var i = 0; i < s.length; i++) {
268 | var ismatch = 0; // by default, no match
269 | for (var j = 0; j < therules.length; j++) {
270 | if (s[i] == therules[j][0]) {
271 | outputstring += therules[j][1]; // write substitution
272 | ismatch = 1; // we have a match, so don't copy over symbol
273 | break; // get outta this for() loop
274 | }
275 | }
276 | // if nothing matches, just copy the symbol over.
277 | if (ismatch == 0) outputstring+= s[i];
278 | }
279 |
280 | return outputstring; // send out the modified string
281 | }
282 |
283 | // this is a custom function that draws turtle commands
284 | function drawIt(k) {
285 |
286 | if (k=='F') { // draw forward
287 | // polar to cartesian based on step and currentangle:
288 | var x1 = x + step*cos(radians(currentangle));
289 | var y1 = y + step*sin(radians(currentangle));
290 | line(x, y, x1, y1); // connect the old and the new
291 |
292 | // update the turtle's position:
293 | x = x1;
294 | y = y1;
295 | } else if (k == '+') {
296 | currentangle += angle; // turn left
297 | } else if (k == '-') {
298 | currentangle -= angle; // turn right
299 | }
300 |
301 | // give me some random color values:
302 | var r = random(128, 255);
303 | var g = random(0, 192);
304 | var b = random(0, 50);
305 | var a = random(50, 100);
306 |
307 | // pick a gaussian (D&D) distribution for the radius:
308 | var radius = 0;
309 | radius += random(0, 15);
310 | radius += random(0, 15);
311 | radius += random(0, 15);
312 | radius = radius/3;
313 |
314 | // draw the stuff:
315 | fill(r, g, b, a);
316 | ellipse(x, y, radius, radius);
317 | }`
318 | },
319 | {
320 | name: 'lerp color',
321 | url: 'https://p5js.org/examples/color-lerp-color.html',
322 | code: `
323 | function setup() {
324 | createCanvas(720, 400);
325 | background(255);
326 | noStroke();
327 | }
328 |
329 | function draw() {
330 | background(255);
331 | from = color(255, 0, 0, 0.2 * 255);
332 | to = color(0, 0, 255, 0.2 * 255);
333 | c1 = lerpColor(from, to, .33);
334 | c2 = lerpColor(from, to, .66);
335 | for (var i = 0; i < 15; i++) {
336 | fill(from);
337 | quad(random(-40, 220), random(height),
338 | random(-40, 220), random(height),
339 | random(-40, 220), random(height),
340 | random(-40, 220), random(height));
341 | fill(c1);
342 | quad(random(140, 380), random(height),
343 | random(140, 380), random(height),
344 | random(140, 380), random(height),
345 | random(140, 380), random(height));
346 | fill(c2);
347 | quad(random(320, 580), random(height),
348 | random(320, 580), random(height),
349 | random(320, 580), random(height),
350 | random(320, 580), random(height));
351 | fill(to);
352 | quad(random(500, 760), random(height),
353 | random(500, 760), random(height),
354 | random(500, 760), random(height),
355 | random(500, 760), random(height));
356 | }
357 | frameRate(5);
358 | }`
359 | }
360 | ];
361 |
--------------------------------------------------------------------------------
/src/genetic/evolution.worker.js:
--------------------------------------------------------------------------------
1 | import { fromJS } from 'immutable';
2 | import { fromJSON, toJSON } from 'transit-immutable-js';
3 |
4 | import { evolvePopulation, getGenotype } from './population';
5 |
6 | import { HISTORY_SIZE } from '../constants';
7 |
8 | const { min } = Math;
9 |
10 | self.onmessage = event => {
11 | const data = fromJSON(event.data);
12 | const population = data.get('population');
13 | const history = data.get('history');
14 |
15 | const updatedHistory = history
16 | .unshift(getGenotype(population, data.get('evolveId')))
17 | .setSize(min(HISTORY_SIZE, history.count() + 1));
18 |
19 | const evolvedPopulation = evolvePopulation(population, updatedHistory);
20 |
21 | self.postMessage(
22 | toJSON(
23 | fromJS({
24 | history: updatedHistory,
25 | population: evolvedPopulation
26 | })
27 | )
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/src/genetic/genotype.js:
--------------------------------------------------------------------------------
1 | import uuid from 'uuid';
2 | import { fromJS } from 'immutable';
3 |
4 | import { chance, clamp, isInt, orderOfMagnitude, random } from '../utils';
5 |
6 | import { MUTATION_CHANCE, LITERAL_TYPES, SPREADS } from '../constants';
7 |
8 | // mutations for different literals
9 | const MUTATIONS = {
10 | [LITERAL_TYPES.CONSTANT]: num => {
11 | const value = orderOfMagnitude(Math.abs(num));
12 |
13 | return num + random(-value * SPREADS.COLOR, value * SPREADS.COLOR);
14 | },
15 |
16 | [LITERAL_TYPES.COLOR]: num => {
17 | return clamp(num + random(-255 * SPREADS.COLOR, 255 * SPREADS.COLOR), 0, 255);
18 | },
19 |
20 | [LITERAL_TYPES.CONTROL]: num => {
21 | const value = orderOfMagnitude(Math.abs(num));
22 | const newNum = num + random(-value * SPREADS.COLOR, value * SPREADS.COLOR);
23 |
24 | return isInt(num) ? Math.round(newNum) : newNum;
25 | }
26 | };
27 |
28 | export const createGenotype = code => {
29 | return fromJS({ code, id: uuid.v4() });
30 | };
31 |
32 | export const crossover = (parentA, parentB) => {
33 | // 50/50 chance that given value comes from this genotype or partner
34 | const code = parentA.get('code').map((v, i) => chance(0.5) ? v : parentB.getIn(['code', i]));
35 |
36 | return createGenotype(code);
37 | };
38 |
39 | export const mutate = (genotype, mutationChance = MUTATION_CHANCE) => {
40 | // MUTATION_CHANCE that given value will be random
41 | const code = genotype.get('code').map(number => {
42 | if (!chance(mutationChance)) {
43 | return number;
44 | }
45 |
46 | const mutated = MUTATIONS[number.get('type')](number.get('value'));
47 |
48 | if (isNaN(number.get('value')) || isNaN(mutated)) {
49 | console.warn('NaN in mutation', mutated, number.toJS());
50 | return number;
51 | }
52 |
53 | return number.set('value', mutated);
54 | });
55 |
56 | return createGenotype(code);
57 | };
58 |
--------------------------------------------------------------------------------
/src/genetic/population.js:
--------------------------------------------------------------------------------
1 | import times from 'lodash.times';
2 | import { createGenotype, crossover, mutate } from './genotype';
3 | import { fromJS, List } from 'immutable';
4 |
5 | import { random } from '../utils';
6 |
7 | const prop = key => obj => obj.get(key);
8 |
9 | const dist = (xs, ys) => Math.sqrt(xs.map((x, i) => Math.pow(x - ys.get(i), 2)).reduce((acc, diff) => acc + diff, 0));
10 |
11 | const calculateFitness = (genotype, bestFits) =>
12 | bestFits
13 | .map(bestFit => dist(bestFit.get('code').map(prop('value')), genotype.get('code').map(prop('value'))))
14 | .reduce((acc, dist) => acc + dist, 0) / bestFits.count();
15 |
16 | const rouletteIdx = (normalizedFitnesses, sumFitnesses) =>
17 | normalizedFitnesses.reduce(
18 | (acc, fitness, idx) => {
19 | if (!acc.idx) {
20 | const newValue = acc.value - fitness;
21 |
22 | return {
23 | value: newValue,
24 | idx: newValue <= 0 ? idx : acc.idx
25 | };
26 | } else {
27 | return acc;
28 | }
29 | },
30 | { value: random(sumFitnesses), idx: undefined }
31 | ).idx;
32 |
33 | export const createPopulation = (populationSize, numbers) => {
34 | // start with random mutated population,
35 | // chance overwritten to 0.5, to make sure it happens
36 | const mutationChance = 0.5;
37 | return fromJS(times(populationSize).map(() => mutate(createGenotype(numbers), mutationChance)));
38 | };
39 |
40 | export const evolvePopulation = (inPopulation, history) => {
41 | let newPopulation = List();
42 |
43 | // add fitnesses to population (the smaller the better)
44 | const population = inPopulation.map(genotype => genotype.set('fitness', calculateFitness(genotype, history)));
45 |
46 | // normalize and sum fitnesses
47 | const maxFitnesses = Math.max(...population.map(prop('fitness')));
48 | const normalizedFitnesses = population.map(genotype => 1 - genotype.get('fitness') / maxFitnesses);
49 | const sumFitnesses = normalizedFitnesses.reduce((acc, fitness) => acc + fitness, 0);
50 |
51 | // roulette for new population
52 | while (newPopulation.count() < population.count()) {
53 | const parentAIdx = rouletteIdx(normalizedFitnesses, sumFitnesses);
54 | const parentBIdx = rouletteIdx(normalizedFitnesses, sumFitnesses);
55 |
56 | const parentA = population.get(parentAIdx);
57 | const parentB = population.get(parentBIdx);
58 |
59 | if (parentAIdx !== parentBIdx) {
60 | const child = mutate(crossover(parentA, parentB));
61 |
62 | newPopulation = newPopulation.push(child);
63 | } else {
64 | // in rare chance both random idxs are the same
65 | // just let the parent live for next iteration
66 | newPopulation = newPopulation.push(parentA);
67 | }
68 | }
69 |
70 | // new population sorted by fitness (again, smaller is better)
71 | return newPopulation
72 | .map(genotype => genotype.set('fitness', calculateFitness(genotype, history)))
73 | .sort((a, b) => a.get('fitness') - b.get('fitness'));
74 | };
75 |
76 | export const getGenotype = (population, id) => population.find(genotype => genotype.get('id') === id);
77 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Cousine');
2 |
3 | * {
4 | padding: 0;
5 | margin: 0;
6 | }
7 |
8 | body {
9 | height: 100%;
10 | background: #181818;
11 | font-family: 'Cousine', monospace;
12 | }
13 |
14 | #root {
15 | min-height: 100vh;
16 | font-size: 13px;
17 | }
18 |
19 | .modal-evolving {
20 | display: table;
21 | height: 100vh;
22 | width: 100%;
23 | }
24 |
25 | .modal-evolving__content {
26 | display: table-cell;
27 | vertical-align: middle;
28 | text-align: center;
29 | color: #181818;
30 | }
31 |
32 | .content-no-scroll {
33 | height: 100vh;
34 | overflow: hidden;
35 | }
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import thunk from 'redux-thunk';
4 | import { applyMiddleware, createStore } from 'redux';
5 | import { connect, Provider } from 'react-redux';
6 |
7 | import CodePreview from './components/code-preview';
8 | import Header from './components/header';
9 | import Info from './components/info';
10 | import Intro from './components/intro';
11 | import Modal from './components/modal';
12 | import Population from './components/population';
13 |
14 | import appStore from './reducers';
15 |
16 | import './index.css';
17 |
18 | const store = createStore(appStore, applyMiddleware(thunk));
19 |
20 | const EvolvingModal = () => (
21 |
22 |
23 | ... evolving ...
24 |
25 |
26 | );
27 |
28 | const App = ({ isInited, isEvolving, showCode, showInfo }) => {
29 | const modalOpened = isEvolving || !!showCode || showInfo;
30 |
31 | return (
32 |
33 | {!isInited ?
:
}
34 |
35 |
36 | {isEvolving && }
37 | {showInfo && }
38 | {!!showCode && }
39 |
40 |
41 | );
42 | };
43 |
44 | const mapStateToProps = state => ({
45 | isInited: !!state.get('inputCode'),
46 | isEvolving: state.get('isEvolving'),
47 | showCode: state.get('showCode'),
48 | showInfo: state.get('showInfo')
49 | });
50 |
51 | const AppConnected = connect(mapStateToProps)(App);
52 |
53 | ReactDOM.render(
54 |
55 |
56 | ,
57 | document.getElementById('root')
58 | );
59 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { fromJS } from 'immutable';
2 |
3 | import { createPopulation } from '../genetic/population';
4 | import { findNumbers } from '../code-transform';
5 |
6 | import { POPULATION_SIZE } from '../constants';
7 |
8 | const isDebug = window.location.search.indexOf('debug') >= 0;
9 |
10 | let parsed;
11 |
12 | if (isDebug) {
13 | try {
14 | parsed = JSON.parse(localStorage.getItem('state'));
15 | } catch (e) {
16 | console.error(e);
17 | }
18 |
19 | window.clearState = () => {
20 | localStorage.setItem('state', null);
21 | window.location.reload();
22 | };
23 | }
24 |
25 | const emptyState = {
26 | isEvolving: false,
27 | inputCode: undefined,
28 | inputNumbers: [],
29 | population: [],
30 | history: [],
31 | showCode: false,
32 | showInfo: false
33 | };
34 |
35 | const initialState = fromJS(parsed || emptyState);
36 |
37 | export default (state = initialState, action) => {
38 | if (action.type === 'ADD_INPUT_CODE') {
39 | const numbers = fromJS(findNumbers(action.code));
40 |
41 | state = state
42 | .set('inputCode', action.code)
43 | .set('inputNumbers', numbers)
44 | .set('population', createPopulation(POPULATION_SIZE, numbers));
45 | }
46 |
47 | if (action.type === 'EVOLVE_GENOTYPE_START') {
48 | state = state.set('isEvolving', true);
49 | }
50 |
51 | if (action.type === 'EVOLVE_GENOTYPE_DONE') {
52 | state = state.set('population', action.population).set('history', action.history).set('isEvolving', false);
53 | }
54 |
55 | if (action.type === 'SHOW_CODE') {
56 | state = state.set('showCode', action.code);
57 | }
58 |
59 | if (action.type === 'HIDE_CODE') {
60 | state = state.set('showCode', false);
61 | }
62 |
63 | if (action.type === 'SHOW_INFO') {
64 | state = state.set('showInfo', true);
65 | }
66 |
67 | if (action.type === 'HIDE_INFO') {
68 | state = state.set('showInfo', false);
69 | }
70 |
71 | if (action.type === 'RESET_APP') {
72 | state = fromJS(emptyState);
73 | }
74 |
75 | if (isDebug) {
76 | localStorage.setItem('state', JSON.stringify(state.toJS()));
77 | }
78 |
79 | return state;
80 | };
81 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import randomSeed from 'random-seed';
2 |
3 | const seed = randomSeed.create();
4 |
5 | export const isInt = n => n % 1 === 0;
6 |
7 | export const orderOfMagnitude = n => {
8 | const eps = 0.000000001;
9 | const order = Math.abs(n) < eps ? 0 : Math.floor(Math.log(n) / Math.LN10 + eps);
10 |
11 | return Math.pow(10, order);
12 | };
13 |
14 | export const random = (...args) => {
15 | if (args.length === 0) {
16 | return seed.random();
17 | } else if (args.length === 1) {
18 | return seed.random() * args[0];
19 | } else {
20 | return seed.random() * Math.abs(args[0] - args[1]) + Math.min(args[0], args[1]);
21 | }
22 | };
23 |
24 | export const chance = percent => random() < percent;
25 |
26 | export const clamp = (v, min, max) => Math.max(min, Math.min(v, max));
27 |
--------------------------------------------------------------------------------
/tests/code-transform-electron-tests/README.md:
--------------------------------------------------------------------------------
1 | This was an early experiment in automatic testing of recast code transforms in browser env with electron.
2 |
3 | It worked for a while, but is currently broken because of changes in AST code in main app.
4 |
5 | I also had some problems with `import` vs `required` but nothing that couldn't be fixed with babel.
6 |
7 | I'm leaving it in here for future references.
8 |
--------------------------------------------------------------------------------
/tests/code-transform-electron-tests/index.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 |
3 | const express = require('express');
4 | const fs = require('fs');
5 | const getPort = require('get-port');
6 | const path = require('path');
7 | const url = require('url');
8 |
9 | const codeTransform = require('../../src/code-transform/').default;
10 |
11 | const { BrowserWindow, app, ipcMain } = require('electron');
12 |
13 | const LIB_URL = "//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.js";
14 | const SLEEP_TIME = 100;
15 |
16 | const log = (args) => console.log(JSON.stringify({
17 | message: args,
18 | level: args.error ? 'error' : 'info',
19 | colors: {
20 | error: args.error ? 'red' : 'green'
21 | }
22 | }));
23 |
24 | let server, mainWindow;
25 |
26 | const displayFile = (port, path, callback) => {
27 | const formated = url.format({
28 | host: `127.0.0.1:${port}`,
29 | path: '/',
30 | protocol: 'http',
31 | query: {
32 | file: path,
33 | t: (new Date()).getTime()
34 | }
35 | });
36 |
37 | mainWindow.webContents.loadURL(formated);
38 | };
39 |
40 | const createServer = (port) => {
41 | server = express();
42 |
43 | server.get('/', (req, res) => {
44 | const { file } = req.query;
45 |
46 | const { code, error } = codeTransform(fs.readFileSync(file));
47 |
48 | log(error ? { file, error: `${error}` } : { file });
49 |
50 | res.send(`
51 |
54 |
55 |
56 |
57 |
75 |
76 |
88 | `);
89 | });
90 |
91 | server.listen(port);
92 | };
93 |
94 | const createWindow = (port) => {
95 | createServer(port);
96 |
97 | mainWindow = new BrowserWindow({
98 | width: 600,
99 | height: 600,
100 | resizable: false
101 | });
102 |
103 | const mainDir = process.argv[2]
104 | const files = fs.readdirSync(mainDir);
105 | let idx = 0;
106 |
107 | const displayNext = () => {
108 | displayFile(port, path.join(mainDir, files[idx]));
109 | idx++;
110 | };
111 |
112 | displayNext();
113 |
114 | ipcMain.on('done', () => {
115 | setTimeout(displayNext, SLEEP_TIME);
116 | });
117 |
118 | ipcMain.on('failure', (_, { error, file }) => {
119 | log({ error: `${error}`, file });
120 | setTimeout(displayNext, SLEEP_TIME);
121 | });
122 |
123 | mainWindow.on('closed', () => {
124 | mainWindow = null
125 | });
126 | };
127 |
128 | const start = (port) => {
129 | app.on('ready', () => createWindow(port));
130 |
131 | app.on('window-all-closed', () => {
132 | if (process.platform !== 'darwin') {
133 | app.quit();
134 | }
135 | });
136 | };
137 |
138 | getPort().then(port => start(port));
139 |
--------------------------------------------------------------------------------
/tests/code-transform-electron-tests/test-code-transform-electron.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ -z "$1" ]; then
4 | echo "Pass directory of js files to test"
5 | exit 0
6 | fi
7 |
8 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9 |
10 | pushd "$DIR"/.. > /dev/null
11 |
12 | NODE_ENV=development ./node_modules/.bin/electron tests/code-transform-electron-tests "$1" | tee code-transform-electron-tests.log | ./node_modules/.bin/garnish
13 |
14 | popd
15 |
--------------------------------------------------------------------------------
/tests/code-transform-test/color-smoke.js:
--------------------------------------------------------------------------------
1 | var np = 300;
2 | var startcol;
3 |
4 | function setup() {
5 | createCanvas(1366, 600);
6 | background(255);
7 | noFill();
8 | noiseSeed(random(100));
9 | startcol = random(255);
10 | }
11 |
12 | function draw() {
13 | // background(51);
14 | beginShape();
15 | var sx, sy;
16 | for (var i = 0; i < np; i++) {
17 | var angle = map(i, 0, np, 0, TWO_PI);
18 | var cx = frameCount * 2 - 200;
19 | var cy = height / 2 + 50 * sin(frameCount / 50);
20 | var xx = 100 * cos(angle + cx / 10);
21 | var yy = 100 * sin(angle + cx / 10);
22 | var v = createVector(xx, yy);
23 | xx = (xx + cx) / 150;
24 | yy = (yy + cy) / 150;
25 | v.mult(1 + 1.5 * noise(xx, yy));
26 | vertex(cx + v.x, cy + v.y);
27 | if (i == 0) {
28 | sx = cx + v.x;
29 | sy = cy + v.y;
30 | }
31 | }
32 | colorMode(HSB);
33 | var hue = cx / 10 - startcol;
34 | if (hue < 0) hue += 255;
35 | stroke(hue, 100, 120);
36 | strokeWeight(0.1);
37 | vertex(sx, sy);
38 | endShape();
39 | if (frameCount > width + 500) {
40 | noLoop();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/code-transform-test/complex-control.js:
--------------------------------------------------------------------------------
1 | var i = 0;
2 | var j = 1;
3 |
4 | var r = 200;
5 | var g = 0;
6 | var b = 120;
7 |
8 | function setup() {
9 | var points = [];
10 |
11 | for (i; i < 20; i++) {
12 | points[i] = i * 100;
13 | }
14 |
15 | while(j < 10) {
16 | j++;
17 | }
18 | }
19 |
20 | function draw() {
21 | background(r, g, b);
22 | }
23 |
--------------------------------------------------------------------------------
/tests/code-transform-test/detection-01.js:
--------------------------------------------------------------------------------
1 | fill(255, 255, 255);
2 |
3 | var x = "ASD";
4 |
5 | while (i < 10) {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/tests/code-transform-test/detection-02.js:
--------------------------------------------------------------------------------
1 | var x = 10
2 | var y = "asd"
3 | var z = "#ccc"
4 |
--------------------------------------------------------------------------------
/tests/code-transform-test/distortion-tests.js:
--------------------------------------------------------------------------------
1 | var cnv;
2 |
3 | var NB_FRAMES = 100;
4 |
5 | var frame_count = 0;
6 |
7 | function activation(t) {
8 | return ((1 - cos(2 * PI * t)) / 2) ** 1;
9 | }
10 |
11 | function object(id) {
12 | this.id = id;
13 |
14 | this.draw = function() {
15 | var t = frame_count % NB_FRAMES / NB_FRAMES;
16 |
17 | var x0 = lerp(0, window.innerWidth, this.id / NB);
18 |
19 | theta = PI / 2;
20 |
21 | var xx = x0;
22 | var yy = 0;
23 |
24 | var Nt = 75;
25 |
26 | var step = window.innerHeight / Nt;
27 |
28 | var turn = lerp(0, 0.4, activation((this.id / NB + 0 * t) % 1));
29 |
30 | stroke(255);
31 | strokeWeight(1);
32 | noFill();
33 | beginShape();
34 |
35 | vertex(xx, yy);
36 |
37 | for (var i = 0; i <= Nt; i++) {
38 | theta += turn * sin(100 * noise(1000) + 2 * PI * (15 * noise(0.2 * this.id / NB, 0.02 * i) + t));
39 | //theta += turn*sin(100*noise(1000)+2*PI*(20*noise(0.02*i)+t + 0.1*sin(2*PI*this.id/NB)));
40 | xx += step * cos(theta);
41 | yy += step * sin(theta);
42 |
43 | var xx2 = lerp(xx, x0, i / Nt * (i / Nt) * (i / Nt));
44 | var yy2 = lerp(yy, lerp(0, window.innerHeight - 0, i / Nt), max(i / Nt, 1 - sqrt(i / Nt)));
45 |
46 | vertex(xx2, yy2);
47 | }
48 | endShape();
49 | };
50 | }
51 |
52 | var Objects = [];
53 | var NB = 10;
54 |
55 | function setup() {
56 | curSeed = 11;
57 | noiseSeed(curSeed);
58 | randomSeed(1);
59 |
60 | cnv = createCanvas(window.innerWidth, window.innerHeight);
61 | //cnv.parent("canvas");
62 |
63 | background(0);
64 |
65 | for (var i = 0; i < NB; i++) {
66 | Objects[i] = new object(i);
67 | }
68 | }
69 |
70 | function mousePressed() {
71 | curSeed = floor(random() * 10000);
72 | noiseSeed(curSeed);
73 | console.log(curSeed);
74 | }
75 |
76 | function draw() {
77 | background(0);
78 |
79 | var t = frame_count % NB_FRAMES / NB_FRAMES;
80 |
81 | for (var i = 0; i < NB; i++)
82 | Objects[i].draw();
83 |
84 | noStroke();
85 | fill(255);
86 |
87 | frame_count++;
88 | if (frame_count <= 100 && frame_count > 80) {
89 | //saveCanvas('s5_'+frame_count+'.png');
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/code-transform-test/flow-field.js:
--------------------------------------------------------------------------------
1 | function Particle() {
2 | this.pos = createVector(random(width), random(height));
3 | this.vel = createVector(0, 0);
4 | this.acc = createVector(0, 0);
5 | this.maxspeed = 2;
6 |
7 | this.prevPos = this.pos.copy();
8 |
9 | this.update = function() {
10 | this.vel.add(this.acc);
11 | this.vel.limit(this.maxspeed);
12 | this.pos.add(this.vel);
13 | this.acc.mult(0);
14 | };
15 |
16 | this.follow = function(vectors) {
17 | var x = floor(this.pos.x / scl);
18 | var y = floor(this.pos.y / scl);
19 | var index = x + y * cols;
20 | var force = vectors[index];
21 | this.applyForce(force);
22 | };
23 |
24 | this.applyForce = function(force) {
25 | this.acc.add(force);
26 | };
27 |
28 | this.show = function() {
29 | stroke(0, 5);
30 | strokeWeight(1);
31 | line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
32 | this.updatePrev();
33 | };
34 |
35 | this.updatePrev = function() {
36 | this.prevPos.x = this.pos.x;
37 | this.prevPos.y = this.pos.y;
38 | };
39 | this.edges = function() {
40 | if (this.pos.x > width) {
41 | this.pos.x = 0;
42 | this.updatePrev();
43 | }
44 | if (this.pos.x < 0) {
45 | this.pos.x = width;
46 | this.updatePrev();
47 | }
48 | if (this.pos.y > height) {
49 | this.pos.y = 0;
50 | this.updatePrev();
51 | }
52 | if (this.pos.y < 0) {
53 | this.pos.y = height;
54 | this.updatePrev();
55 | }
56 | };
57 | }
58 |
59 | var inc = 0.1;
60 | var scl = 10;
61 | var cols, rows;
62 |
63 | var zoff = 0;
64 |
65 |
66 | var particles = [];
67 |
68 | var flowfield;
69 |
70 | function setup() {
71 | createCanvas(800, 600);
72 | cols = floor(width / scl);
73 | rows = floor(height / scl);
74 |
75 | flowfield = new Array(cols * rows);
76 |
77 | for (var i = 0; i < 500; i++) {
78 | particles[i] = new Particle();
79 | }
80 | background(255);
81 | }
82 |
83 | function draw() {
84 | var yoff = 0;
85 | for (var y = 0; y < rows; y++) {
86 | var xoff = 0;
87 | for (var x = 0; x < cols; x++) {
88 | var index = x + y * cols;
89 | var angle = noise(xoff, yoff, zoff) * TWO_PI * 4;
90 | var v = p5.Vector.fromAngle(angle);
91 | v.setMag(1);
92 | flowfield[index] = v;
93 | xoff += inc;
94 | stroke(0, 50);
95 | // push();
96 | // translate(x * scl, y * scl);
97 | // rotate(v.heading());
98 | // strokeWeight(1);
99 | // line(0, 0, scl, 0);
100 | // pop();
101 | }
102 | yoff += inc;
103 |
104 | zoff += 0.0003;
105 | }
106 |
107 | for (var i = 0; i < particles.length; i++) {
108 | particles[i].follow(flowfield);
109 | particles[i].update();
110 | particles[i].edges();
111 | particles[i].show();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/tests/code-transform-test/lorenz.js:
--------------------------------------------------------------------------------
1 | var x = 1;
2 | var y = 0;
3 | var z = 0;
4 |
5 | var sigma = 10;
6 | var rho = 28;
7 | var beta = 8 / 3;
8 |
9 | var points = [];
10 | var numPoints = 5000;
11 |
12 | function setup() {
13 | createCanvas(window.innerWidth, window.innerHeight, WEBGL);
14 |
15 | for (var i = 0; i < numPoints; i++) {
16 | var dt = 0.01;
17 | var dx = sigma * (y - x) * dt;
18 | var dy = (x * (rho - z) - y) * dt;
19 | var dz = (x * y - beta * z) * dt;
20 | x += dx;
21 | y += dy;
22 | z += dz;
23 | points.push(createVector(x, y, z));
24 | }
25 | }
26 |
27 | function draw() {
28 | background(100);
29 |
30 | ambientLight(255);
31 | fill(120);
32 |
33 | scale(3);
34 | rotateX(3);
35 | rotateY(3);
36 | rotateZ(3);
37 |
38 | beginShape();
39 | for (var i = 0; i < points.length; i++) {
40 | vertex(points[i].x, points[i].y, points[i].z);
41 | }
42 | endShape();
43 | }
44 |
--------------------------------------------------------------------------------
/tests/code-transform-test/neon-waves.js:
--------------------------------------------------------------------------------
1 | var lines = [];
2 | var gaps = [];
3 | var amounts;
4 |
5 | var colourSchemes = [[0, 60], [61, 160], [161, 250], [251, 360]];
6 |
7 | var schemeNumber;
8 | var currentCol;
9 |
10 | function setup() {
11 | createCanvas(400, 400);
12 | background(0);
13 | colorMode(HSB);
14 | setupLines();
15 | }
16 |
17 | var setupLines = function() {
18 | // select the amount of noise waves to have
19 | amounts = int(random(3, 6));
20 | for (var i = 0; i < amounts; i++) {
21 | lines[i] = [];
22 | gaps[i] = [];
23 | }
24 |
25 | for (var a = 0; a < amounts; a++) {
26 | noiseStart = random(3000);
27 | noiseInc = random(0.001, 0.01);
28 | noiseOff = 0.0;
29 |
30 | total = 0;
31 | // make the original noise wave
32 | for (var i = 0; i < width; i++) {
33 | lines[a][i] = map(noise(noiseStart + noiseOff), 0, 1, 0, height);
34 | noiseOff += noiseInc;
35 | total += lines[a][i];
36 | }
37 |
38 | var avg = total / lines[a].length;
39 | var maximum = max(lines[a]) - avg;
40 | var minimum = min(lines[a]) - avg;
41 | // change the density of the waves depending on how high/low the point is
42 | for (var i = 0; i < width; i++) {
43 | gaps[a][i] = map(lines[a][i] - avg, minimum, maximum, 10, 25);
44 | }
45 | }
46 |
47 | // select a random range of hues
48 | schemeNumber = int(random(colourSchemes.length));
49 | currentCol = random(colourSchemes[schemeNumber][0], colourSchemes[schemeNumber][1]);
50 | };
51 |
52 | var pos = 0;
53 | var current = 0;
54 |
55 | function draw() {
56 | // change strokeweight based on wave density
57 | strokeWeight(map(gaps[current][pos], 10, 25, 0.3, 3));
58 |
59 | // loop through the height of the screen
60 | for (var y = 0; y < height; y += gaps[current][pos]) {
61 | stroke(currentCol + y / 9, 255, 255);
62 | line(
63 | pos - 1,
64 | lines[current][pos - 1] + y + gaps[current][pos - 1],
65 | pos,
66 | lines[current][pos] + y + gaps[current][pos]
67 | );
68 | }
69 | // if the x position hasn't reached the end
70 | if (pos < width) {
71 | pos += 1;
72 | } else {
73 | // go onto the next wave
74 | if (current < amounts - 1) {
75 | current += 1;
76 | pos = 0;
77 | currentCol = random(colourSchemes[schemeNumber][0], colourSchemes[schemeNumber][1]);
78 | }
79 | }
80 | }
81 |
82 | function mousePressed() {
83 | // reset the screen
84 | background(0);
85 | pos = 0;
86 | current = 0;
87 | setupLines();
88 | }
89 |
--------------------------------------------------------------------------------
/tests/code-transform-test/p5-l-system.js:
--------------------------------------------------------------------------------
1 | // TURTLE STUFF:
2 | var x, y; // the current position of the turtle
3 | var currentangle = 0; // which way the turtle is pointing
4 | var step = 20; // how much the turtle moves with each 'F'
5 | var angle = 90; // how much the turtle turns with a '-' or '+'
6 |
7 | // LINDENMAYER STUFF (L-SYSTEMS)
8 | var thestring = 'A'; // "axiom" or start of the string
9 | var numloops = 5; // how many iterations to pre-compute
10 | var therules = []; // array for rules
11 | therules[0] = ['A', '-BF+AFA+FB-']; // first rule
12 | therules[1] = ['B', '+AF-BFB-FA+']; // second rule
13 |
14 | var whereinstring = 0; // where in the L-system are we?
15 |
16 | function setup() {
17 | createCanvas(710, 400);
18 | background(255);
19 | stroke(0, 0, 0, 255);
20 |
21 | // start the x and y position at lower-left corner
22 | x = 0;
23 | y = height - 1;
24 |
25 | // COMPUTE THE L-SYSTEM
26 | for (var i = 0; i < numloops; i++) {
27 | thestring = lindenmayer(thestring);
28 | }
29 | }
30 |
31 | function draw() {
32 | // draw the current character in the string:
33 | drawIt(thestring[whereinstring]);
34 |
35 | // increment the point for where we're reading the string.
36 | // wrap around at the end.
37 | whereinstring++;
38 | if (whereinstring > thestring.length - 1) whereinstring = 0;
39 | }
40 |
41 | // interpret an L-system
42 | function lindenmayer(s) {
43 | var outputstring = ''; // start a blank output string
44 |
45 | // iterate through 'therules' looking for symbol matches:
46 | for (var i = 0; i < s.length; i++) {
47 | var ismatch = 0; // by default, no match
48 | for (var j = 0; j < therules.length; j++) {
49 | if (s[i] == therules[j][0]) {
50 | outputstring += therules[j][1]; // write substitution
51 | ismatch = 1; // we have a match, so don't copy over symbol
52 | break; // get outta this for() loop
53 | }
54 | }
55 | // if nothing matches, just copy the symbol over.
56 | if (ismatch == 0) outputstring += s[i];
57 | }
58 |
59 | return outputstring; // send out the modified string
60 | }
61 |
62 | // this is a custom function that draws turtle commands
63 | function drawIt(k) {
64 | if (k == 'F') {
65 | // draw forward
66 | // polar to cartesian based on step and currentangle:
67 | var x1 = x + step * cos(radians(currentangle));
68 | var y1 = y + step * sin(radians(currentangle));
69 | line(x, y, x1, y1); // connect the old and the new
70 |
71 | // update the turtle's position:
72 | x = x1;
73 | y = y1;
74 | } else if (k == '+') {
75 | currentangle += angle; // turn left
76 | } else if (k == '-') {
77 | currentangle -= angle; // turn right
78 | }
79 |
80 | // give me some random color values:
81 | var r = random(128, 255);
82 | var g = random(0, 192);
83 | var b = random(0, 50);
84 | var a = random(50, 100);
85 |
86 | // pick a gaussian (D&D) distribution for the radius:
87 | var radius = 0;
88 | radius += random(0, 15);
89 | radius += random(0, 15);
90 | radius += random(0, 15);
91 | radius = radius / 3;
92 |
93 | // draw the stuff:
94 | fill(r, g, b, a);
95 | ellipse(x, y, radius, radius);
96 | }
97 |
--------------------------------------------------------------------------------
/tests/code-transform-test/p5-noise-wave.js:
--------------------------------------------------------------------------------
1 | var yoff = 0.0; // 2nd dimension of perlin noise
2 |
3 | function setup() {
4 | createCanvas(710, 400);
5 | }
6 |
7 | function draw() {
8 | background(51);
9 |
10 | fill(255);
11 | // We are going to draw a polygon out of the wave points
12 | beginShape();
13 |
14 | var xoff = 0; // Option #1: 2D Noise
15 | // var xoff = yoff; // Option #2: 1D Noise
16 |
17 | // Iterate over horizontal pixels
18 | for (var x = 0; x <= width; x += 10) {
19 | // Calculate a y value according to noise, map to
20 |
21 | // Option #1: 2D Noise
22 | var y = map(noise(xoff, yoff), 0, 1, 200, 300);
23 |
24 | // Option #2: 1D Noise
25 | // var y = map(noise(xoff), 0, 1, 200,300);
26 |
27 | // Set the vertex
28 | vertex(x, y);
29 | // Increment x dimension for noise
30 | xoff += 0.05;
31 | }
32 | // increment y dimension for noise
33 | yoff += 0.01;
34 | vertex(width, height);
35 | vertex(0, height);
36 | endShape(CLOSE);
37 | }
38 |
--------------------------------------------------------------------------------
/tests/code-transform-test/p5-particle-sys.js:
--------------------------------------------------------------------------------
1 | var system;
2 |
3 | function setup() {
4 | createCanvas(720, 400);
5 | system = new ParticleSystem(createVector(width / 2, 50));
6 | }
7 |
8 | function draw() {
9 | background(51);
10 | system.addParticle();
11 | system.run();
12 | }
13 |
14 | // A simple Particle class
15 | var Particle = function(position) {
16 | this.acceleration = createVector(0, 0.05);
17 | this.velocity = createVector(random(-1, 1), random(-1, 0));
18 | this.position = position.copy();
19 | this.lifespan = 255.0;
20 | };
21 |
22 | Particle.prototype.run = function() {
23 | this.update();
24 | this.display();
25 | };
26 |
27 | // Method to update position
28 | Particle.prototype.update = function() {
29 | this.velocity.add(this.acceleration);
30 | this.position.add(this.velocity);
31 | this.lifespan -= 2;
32 | };
33 |
34 | // Method to display
35 | Particle.prototype.display = function() {
36 | stroke(200, this.lifespan);
37 | strokeWeight(2);
38 | fill(127, this.lifespan);
39 | ellipse(this.position.x, this.position.y, 12, 12);
40 | };
41 |
42 | // Is the particle still useful?
43 | Particle.prototype.isDead = function() {
44 | if (this.lifespan < 0) {
45 | return true;
46 | } else {
47 | return false;
48 | }
49 | };
50 |
51 | var ParticleSystem = function(position) {
52 | this.origin = position.copy();
53 | this.particles = [];
54 | };
55 |
56 | ParticleSystem.prototype.addParticle = function() {
57 | this.particles.push(new Particle(this.origin));
58 | };
59 |
60 | ParticleSystem.prototype.run = function() {
61 | for (var i = this.particles.length - 1; i >= 0; i--) {
62 | var p = this.particles[i];
63 | p.run();
64 | if (p.isDead()) {
65 | this.particles.splice(i, 1);
66 | }
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/tests/code-transform-test/p5-sine-wave.js:
--------------------------------------------------------------------------------
1 | var xspacing = 16; // Distance between each horizontal location
2 | var w; // Width of entire wave
3 | var theta = 0.0; // Start angle at 0
4 | var amplitude = 75.0; // Height of wave
5 | var period = 500.0; // How many pixels before the wave repeats
6 | var dx; // Value for incrementing x
7 | var yvalues; // Using an array to store height values for the wave
8 |
9 | function setup() {
10 | createCanvas(710, 400);
11 | w = width + 16;
12 | dx = TWO_PI / period * xspacing;
13 | yvalues = new Array(floor(w / xspacing));
14 | }
15 |
16 | function draw() {
17 | background(0);
18 | calcWave();
19 | renderWave();
20 | }
21 |
22 | function calcWave() {
23 | // Increment theta (try different values for
24 | // 'angular velocity' here)
25 | theta += 0.02;
26 |
27 | // For every x value, calculate a y value with sine function
28 | var x = theta;
29 | for (var i = 0; i < yvalues.length; i++) {
30 | yvalues[i] = sin(x) * amplitude;
31 | x += dx;
32 | }
33 | }
34 |
35 | function renderWave() {
36 | noStroke();
37 | fill(255);
38 | // A simple way to draw the wave with an ellipse at each location
39 | for (var x = 0; x < yvalues.length; x++) {
40 | ellipse(x * xspacing, height / 2 + yvalues[x], 16, 16);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/code-transform-test/shiffman-supershapes.js:
--------------------------------------------------------------------------------
1 | // Daniel Shiffman
2 | // http://codingrainbow.com
3 | // http://patreon.com/codingrainbow
4 | // Code for: https://youtu.be/ksRoh-10lak
5 |
6 | var n1 = 0.3;
7 | var n2 = 0.3;
8 | var n3 = 0.3;
9 | var m = 5;
10 | var a = 1;
11 | var b = 1;
12 |
13 | var osc = 0;
14 |
15 | function setup() {
16 | createCanvas(400, 400);
17 | }
18 |
19 | function supershape(theta) {
20 |
21 | var part1 = (1 / a) * cos(theta * m / 4);
22 | part1 = abs(part1);
23 | part1 = pow(part1, n2);
24 |
25 | var part2 = (1 / b) * sin(theta * m / 4);
26 | part2 = abs(part2);
27 | part2 = pow(part2, n3);
28 |
29 | var part3 = pow(part1 + part2, 1 / n1);
30 |
31 | if (part3 === 0) {
32 | return 0;
33 | }
34 |
35 | return (1 / part3);
36 | }
37 |
38 |
39 | function draw() {
40 | m = map(sin(osc), -1, 1, 4, 20);
41 | osc += 0.02;
42 |
43 | background(51);
44 | translate(width / 2, height / 2);
45 |
46 | stroke(255);
47 | noFill();
48 |
49 | var radius = 100;
50 |
51 | var total = 200;
52 | var increment = TWO_PI / total;
53 |
54 | beginShape();
55 | for (var angle = 0; angle < TWO_PI; angle += increment) {
56 | var r = supershape(angle);
57 | var x = radius * r * cos(angle);
58 | var y = radius * r * sin(angle);
59 |
60 | vertex(x, y);
61 | }
62 | endShape(CLOSE);
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/tests/code-transform-test/simple-drawing.js:
--------------------------------------------------------------------------------
1 | function setup() {
2 | createCanvas(window.innerWidth, window.innerHeight);
3 | }
4 |
5 | function draw() {
6 | background(20);
7 | fill(120, 0, 120);
8 |
9 | ellipse(20, 20, 10, 10);
10 | ellipse(60, 20, 10, 10);
11 | }
12 |
--------------------------------------------------------------------------------