67 | This project uses a particle simulation to visualize a field of directional vectors. It uses Perlin Noise to
69 | construct a field of random (but related) forces in horizontal and vertical directions (that
70 | change over time). The project uses the P5js
71 | library, and is heavily based on this tutorial
73 | series and the corresponding code.
75 | Source code and additional information are available on GitHub.
77 |
78 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/js/controls.js:
--------------------------------------------------------------------------------
1 |
2 | // Write a function to create a slider
3 | function makeSlider(label, minVal = 0, maxVal = 50, value = 10, step = 1, parent = createDiv(), update = () => {}) {
4 | let wrapper = createDiv(label);
5 | wrapper.parent(parent);
6 | wrapper.class("slider");
7 | let slider = createSlider(minVal, maxVal, value, step);
8 | slider.input(update); // function to do on update
9 | slider.class("form-control-range")
10 | slider.parent(wrapper);
11 | return (slider);
12 | }
13 |
14 | // Function to make a button
15 | function makeButton(text, parent, callback, type = "not_modal") {
16 | let buttonWrapper = createDiv();
17 | buttonWrapper.class("button-wrapper");
18 | let button = createButton(text);
19 | button.class("btn")
20 | if(type === "modal") {
21 | button.attribute("data-toggle", "modal")
22 | button.attribute("data-target", "#exampleModal")
23 | }
24 | button.parent(buttonWrapper)
25 | buttonWrapper.parent(parent);
26 | button.mousePressed(callback);
27 | }
28 |
29 | // Function to make a color picker
30 | function makeColorPicker(label = "Pick a color", startColor = "red", parent = createDiv(), update = () => {}) {
31 | let wrapper = createDiv(label);
32 | wrapper.class("color-picker");
33 | wrapper.parent(parent)
34 | let picker = createColorPicker(startColor)
35 | picker.input(() => update(picker.value()));
36 | picker.parent(wrapper);
37 | picker.class("form-control-range")
38 | return (picker);
39 | }
--------------------------------------------------------------------------------
/js/particle.js:
--------------------------------------------------------------------------------
1 | // Daniel Shiffman
2 | // http://codingtra.in
3 | // http://patreon.com/codingtrain
4 | // Code for: https://youtu.be/BjoM9oKOAKY
5 |
6 | function Particle(cellWidth = 400, cellHeight = 400) {
7 | this.pos = createVector(random(width), random(height));
8 | this.vel = createVector(0, 0);
9 | this.acc = createVector(0, 0);
10 | this.maxspeed = 2;
11 |
12 | this.prevPos = this.pos.copy();
13 |
14 | this.update = function () {
15 | this.vel.add(this.acc);
16 | this.vel.limit(this.maxspeed);
17 | this.pos.add(this.vel);
18 | this.acc.mult(0);
19 | };
20 |
21 | this.follow = function (vectors) {
22 | var x = floor(this.pos.x / cellWidth);
23 | var y = floor(this.pos.y / cellHeight);
24 | var index = x + y * ncol;
25 | var force = vectors[index];
26 | this.applyForce(force);
27 | };
28 |
29 | this.applyForce = function (force) {
30 | this.acc.add(force);
31 | };
32 |
33 | this.getColor = function() {
34 | let color = strokeColorPicker.color()._array.slice(0, 3)
35 | .concat(opacitySlider.value())
36 | .map((d) => d * 100);
37 | return(color);
38 | }
39 | this.show = function () {
40 | stroke(this.getColor());
41 |
42 | strokeWeight(.3);
43 | line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
44 | this.updatePrev();
45 | };
46 |
47 | this.updatePrev = function () {
48 | this.prevPos.x = this.pos.x;
49 | this.prevPos.y = this.pos.y;
50 | };
51 |
52 | this.edges = function () {
53 | if (this.pos.x > width | this.pos.x < 0 | this.pos.y > height | this.pos.y < 0) {
54 | this.pos = createVector(random(width), random(height));
55 | this.updatePrev();
56 | }
57 | };
58 | }
--------------------------------------------------------------------------------
/js/sketch.js:
--------------------------------------------------------------------------------
1 | // Main script to construct the noise field
2 |
3 | // "Global" variables
4 | let X_START = 0;
5 | const Y_START = 0;
6 | let xoff = 0;
7 | let yoff = 0;
8 | let zoff = 0;
9 | let particles = [];
10 | let flowfield = [];
11 | let canvas;
12 | let nrow, ncol, rectWidth, rectHeight;
13 | let xIncrementSlider, yIncrementSlider, zIncrementSlider, particleSlider, opacitySlider, strokeColorPicker, backgroundColorPicker;
14 |
15 | function makeControls() {
16 | // Controls
17 | let controlWrapper = createDiv().id("control-wrapper");
18 | let controlHeader = createDiv("