├── 2021
├── README.md
├── day_1
│ ├── day_1.js
│ ├── day_1.pde
│ └── genuary_1.gif
├── day_10
│ ├── day_10.js
│ ├── day_10.pde
│ └── genuary_10.gif
├── day_11
│ ├── day_11.js
│ ├── day_11.pde
│ └── genuary_11.gif
├── day_12
│ ├── day_12.js
│ ├── day_12.pde
│ └── genuary_12.gif
├── day_13
│ ├── day_13.js
│ ├── day_13.pde
│ └── genuary_13.gif
├── day_14
│ ├── day_14.js
│ ├── day_14.pde
│ └── genuary_14.gif
├── day_15
│ ├── day_15.js
│ ├── day_15.pde
│ └── genuary_15.gif
├── day_16
│ ├── day_16.js
│ ├── day_16.pde
│ └── genuary_16.gif
├── day_17
│ ├── day_17.js
│ ├── day_17.pde
│ └── genuary_17.gif
├── day_18
│ ├── day_18.js
│ ├── day_18.pde
│ └── genuary_18.gif
├── day_19
│ ├── day_19.js
│ ├── day_19.pde
│ └── genuary_19.gif
├── day_2
│ ├── day_2.js
│ ├── day_2.pde
│ └── genuary_2.gif
├── day_20
│ ├── day_20.js
│ ├── day_20.pde
│ └── genuary_20.gif
├── day_21
│ ├── day_21.js
│ ├── day_21.pde
│ └── genuary_21.gif
├── day_22
│ ├── day_22.js
│ ├── day_22.pde
│ └── genuary_22.gif
├── day_23
│ ├── day_23.js
│ ├── day_23.pde
│ └── genuary_23.gif
├── day_24
│ ├── day_24.js
│ ├── day_24.pde
│ └── genuary_24.gif
├── day_25
│ ├── day_25.js
│ ├── day_25.pde
│ └── genuary_25.gif
├── day_26
│ ├── day_26.js
│ ├── day_26.pde
│ └── genuary_26.gif
├── day_27
│ ├── day_27.js
│ ├── day_27.pde
│ └── genuary_27.gif
├── day_28
│ ├── day_28.js
│ ├── day_28.pde
│ └── genuary_28.gif
├── day_29
│ ├── day_29.js
│ ├── day_29.pde
│ └── genuary_29.gif
├── day_3
│ ├── day_3.js
│ ├── day_3.pde
│ └── genuary_3.gif
├── day_30
│ └── day_30.js
├── day_4
│ ├── day_4.js
│ ├── day_4.pde
│ └── genuary_4.gif
├── day_5
│ ├── day_5.js
│ ├── day_5.pde
│ └── genuary_5.gif
├── day_6
│ ├── day_6.js
│ ├── day_6.pde
│ └── genuary_6.gif
├── day_7
│ ├── day_7.js
│ ├── day_7.pde
│ └── genuary_7.gif
├── day_8
│ ├── day_8.js
│ ├── day_8.pde
│ └── genuary_8.gif
└── day_9
│ ├── day_9.js
│ ├── day_9.pde
│ └── genuary_9.gif
├── 2023
├── README.md
├── day_1
│ ├── day_1.js
│ ├── day_1.pde
│ └── genuary_1.gif
├── day_10
│ ├── day_10.pde
│ └── genuary_10.gif
├── day_11
│ ├── day_11.pde
│ └── genuary_11.gif
├── day_12
│ ├── day_12.pde
│ └── genuary_12.gif
├── day_2
│ ├── day_2.js
│ └── genuary_2.png
├── day_3
│ ├── day_3.pde
│ └── genuary_3.gif
├── day_4
│ ├── day_4.pde
│ └── genuary_4.gif
├── day_5
│ ├── day_5.pde
│ └── genuary_5.gif
├── day_6
│ ├── day_6.js
│ ├── genuary_6.gif
│ └── genuary_6.pde
├── day_7
│ ├── day_7.js
│ └── genuary_7.png
├── day_8
│ ├── day_8.pde
│ └── genuary_8.gif
└── day_9
│ ├── day_9.pde
│ └── genuary_9.gif
├── LICENSE
└── README.md
/2021/README.md:
--------------------------------------------------------------------------------
1 | ## 2021 edition
2 |
3 | | [](./day_1/day_1.js)
`// TRIPLE NESTED LOOP` | [](./day_2/day_2.js)
[Rule 30](https://www.wolframalpha.com/input/?i=rule+30) ([elementary cellular automaton](https://en.wikipedia.org/wiki/Rule_30)) | [](./day_3/day_3.js)
Make something human. |
4 | | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
5 | | [](./day_4/day_4.js)
Small areas of symmetry. | [](./day_5/day_5.js)
Do some code golf! | [](./day_6/day_6.js)
Triangle subdivision. |
6 | | [](./day_7/day_7.js)
Generate some rules, then follow them by hand on paper. | [](./day_8/day_8.js)
Curve only. | [](./day_9/day_9.js)
Interference patterns. |
7 | | [](./day_10/day_10.js)
`// TREE` | [](./day_11/day_11.js)
Use a non-computer random source | [](./day_12/day_12.js)
Use an API |
8 | | [](./day_13/day_13.js)
Do not repeat. | [](./day_14/day_14.js)
`// SUBDIVISION` | [](./day_15/day_15.js)
Let someone else decide the rules. |
9 | | [](./day_16/day_16.js)
Circles only | [](./day_17/day_17.js)
Draw a line, pick a new color, move a bit. | [](./day_18/day_18.js)
One process grows, another process prunes. |
10 | | [](./day_19/day_19.js)
Increase the randomness along the Y-axis. | [](./day_20/day_20.js)
No loops. | [](./day_21/day_21.js)
`DRAW(x);` |
11 | | [](./day_22/day_22.js)
Draw a line. Wrong answers only. | [](./day_23/day_23.js)
**#264653** **#2a9d8f** ... | [](./day_24/day_24.js)
500 lines |
12 | | [](./day_25/day_25.js)
Make a grid of permutations of something. | [](./day_26/day_26.js)
2D perspective | [](./day_27/day_27.js)
Monochrome gradients without lines. |
13 | | [](./day_28/day_28.js)
Use sound. | [](./day_29/day_29.js)
Any shape, none can touch. | |
14 | | | | |
15 | | | | |
16 |
--------------------------------------------------------------------------------
/2021/day_1/day_1.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // The number of circles
5 | const nCircles = 15;
6 |
7 | // The radius of the biggest circle
8 | const firstRadius = 150;
9 |
10 | let offset = 0;
11 |
12 | /**
13 | * Give the angle on a circle at that index
14 | */
15 | function angleFromIndex(index, steps) {
16 | return index * (TWO_PI / steps);
17 | }
18 |
19 | /**
20 | * Translate on a circle at given angle
21 | */
22 | function translateOnCircle(angle, radius) {
23 | translate(cos(angle) * radius, sin(angle) * radius);
24 | }
25 |
26 |
27 | function setup() {
28 | createCanvas(500, 500);
29 | }
30 |
31 | function draw() {
32 | background("#003344");
33 |
34 | // Go to the center
35 | translate(width / 2, height / 2);
36 |
37 | // Display first circle
38 | noFill();
39 | stroke(255);
40 | circle(0, 0, firstRadius * 2);
41 |
42 | let angle;
43 |
44 | // First loop
45 | for (let i = 0; i < nCircles; i++) {
46 | push();
47 |
48 | // Compute angle and radius
49 | angle = angleFromIndex(i, nCircles);
50 | const secondRadius = cos(angle + offset) * firstRadius / 2;
51 |
52 | // Move to the circle
53 | translateOnCircle(angle, firstRadius);
54 |
55 | // Draw it
56 | circle(0, 0, secondRadius * 2);
57 |
58 | // Second loop
59 | for (let j = 0; j < nCircles; j++) {
60 | push();
61 |
62 | angle = angleFromIndex(j, nCircles);
63 | const thirdRadius = sin(angle - offset * 2) * secondRadius / 4;
64 |
65 | translateOnCircle(angleFromIndex(j, nCircles), secondRadius);
66 | circle(0, 0, thirdRadius * 2);
67 |
68 | // Third nested loop!
69 | for (let k = 0; k < nCircles / 2; k++) {
70 | push();
71 |
72 | angle = angleFromIndex(k, nCircles);
73 | const fourthRadius = cos(angle - offset * 4) * thirdRadius / 2;
74 |
75 | translateOnCircle(angleFromIndex(k, nCircles / 2), thirdRadius);
76 | circle(0, 0, fourthRadius * 2);
77 |
78 | pop();
79 | }
80 |
81 | pop();
82 | }
83 |
84 | pop();
85 | }
86 |
87 | // Increase the offset
88 | offset += 0.01;
89 | }
90 |
--------------------------------------------------------------------------------
/2021/day_1/day_1.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // The number of circles
5 | int nCircles = 15;
6 |
7 | // The radius of the biggest circle
8 | int firstRadius = 150;
9 |
10 | float offset = 0;
11 |
12 | /**
13 | * Give the angle on a circle at that index
14 | */
15 | float angleFromIndex(int index, int steps) {
16 | return index * (TWO_PI / (float) steps);
17 | }
18 |
19 | /**
20 | * Translate on a circle at given angle
21 | */
22 | void translateOnCircle(float angle, float radius) {
23 | translate(cos(angle) * radius, sin(angle) * radius);
24 | }
25 |
26 |
27 | void setup() {
28 | size(500, 500);
29 | }
30 |
31 | void draw() {
32 | background(#003344);
33 |
34 | // Go to the center
35 | translate(width / 2, height / 2);
36 |
37 | // Display first circle
38 | noFill();
39 | stroke(255);
40 | circle(0, 0, firstRadius * 2);
41 |
42 | float angle;
43 |
44 | // First loop
45 | for (int i = 0; i < nCircles; i++) {
46 | push();
47 |
48 | // Compute angle and radius
49 | angle = angleFromIndex(i, nCircles);
50 | float secondRadius = cos(angle + offset) * firstRadius / 2;
51 |
52 | // Move to the circle
53 | translateOnCircle(angle, firstRadius);
54 |
55 | // Draw it
56 | circle(0, 0, secondRadius * 2);
57 |
58 | // Second loop
59 | for (int j = 0; j < nCircles; j++) {
60 | push();
61 |
62 | angle = angleFromIndex(j, nCircles);
63 | float thirdRadius = sin(angle - offset * 2) * secondRadius / 4;
64 |
65 | translateOnCircle(angleFromIndex(j, nCircles), secondRadius);
66 | circle(0, 0, thirdRadius * 2);
67 |
68 | // Third nested loop!
69 | for (int k = 0; k < nCircles / 2; k++) {
70 | push();
71 |
72 | angle = angleFromIndex(k, nCircles);
73 | float fourthRadius = cos(angle - offset * 4) * thirdRadius / 2;
74 |
75 | translateOnCircle(angleFromIndex(k, nCircles / 2), thirdRadius);
76 | circle(0, 0, fourthRadius * 2);
77 |
78 | pop();
79 | }
80 |
81 | pop();
82 | }
83 |
84 | pop();
85 | }
86 |
87 | // Increase the offset
88 | offset += 0.01;
89 | }
90 |
--------------------------------------------------------------------------------
/2021/day_1/genuary_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_1/genuary_1.gif
--------------------------------------------------------------------------------
/2021/day_10/day_10.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Particle class containing a vector, speed and offset
6 | */
7 | class Particle {
8 | constructor(x, y, offset, initialLife, speed) {
9 | this.location = createVector(x, y);
10 | this.offset = offset;
11 | this.initialLife = initialLife;
12 | this.life = initialLife;
13 | this.speed = speed;
14 | }
15 |
16 | /**
17 | * Returns normalized age from 1 to 0
18 | */
19 | getAge() {
20 | return constrain(map(this.life, this.initialLife, 0, 0, 1), 0, 1);
21 | }
22 |
23 | /**
24 | * Return a new particle when branching at the same location
25 | */
26 | branch() {
27 | return new Particle(this.location.x, this.location.y, this.offset + 5, this.life, this.speed - 0.05);
28 | }
29 |
30 | /**
31 | * Update the particle, add noise and decrease life
32 | */
33 | update() {
34 | const n = noise((this.offset + this.location.x) * 0.1, (this.offset + this.location.y) * 0.1);
35 |
36 | this.location.add(p5.Vector.fromAngle(((n * 2) - 1) * this.getAge() * TWO_PI + HALF_PI).mult(-this.speed));
37 |
38 | this.life -= this.speed;
39 | }
40 |
41 | /**
42 | * Display transparent white dot
43 | */
44 | display() {
45 | stroke(255, 50);
46 | strokeWeight(2);
47 | point(this.location.x, this.location.y);
48 | }
49 | }
50 |
51 | /**
52 | * Create random particles at the bottom of the screen
53 | * the spread controls the size of the trunk
54 | */
55 | function createRandomParticles(spread, margin) {
56 | const particles = [];
57 |
58 | for (let x = (width - spread) / 2; x < (width + spread) / 2; x++) {
59 | const initialLife = random(300, 600);
60 | const randomSpeed = random(0.2, 2);
61 | particles.push(new Particle(x, height - margin, x / 2, initialLife, randomSpeed));
62 | }
63 |
64 | return particles;
65 | }
66 |
67 | let particles;
68 | const spread = 100;
69 | const margin = 50;
70 |
71 | function setup() {
72 | createCanvas(500, 500);
73 |
74 | particles = createRandomParticles(spread, margin);
75 |
76 | // Only need to draw the background once in setup
77 | background("#003344");
78 | }
79 |
80 | function draw() {
81 |
82 | // Loop through particles
83 | for (let i = particles.length - 1; i >= 0; i--) {
84 | const particle = particles[i];
85 |
86 | particle.display();
87 | particle.update();
88 |
89 | const age = particle.getAge();
90 |
91 | // Delete if dead
92 | if (particle.life <= 0) {
93 | particles.pop();
94 | }
95 |
96 | // Branch condition
97 | if (random(1) < age * 0.05) {
98 | // Display an orange dot at that point
99 | stroke("#ff9900");
100 | strokeWeight(age * 10);
101 | point(particle.location.x, particle.location.y);
102 |
103 | // Add the new particle
104 | particles.push(particle.branch());
105 | }
106 | }
107 |
108 | // Stop when there's no more particles
109 | if (particles.length == 0) {
110 | noLoop();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/2021/day_10/day_10.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * Particle class containing a vector, speed and offset
8 | */
9 | class Particle {
10 | PVector location;
11 | float offset, initialLife, life, speed;
12 |
13 | Particle(float x, float y, float offset, float initialLife, float speed) {
14 | this.location = new PVector(x, y);
15 | this.offset = offset;
16 | this.initialLife = initialLife;
17 | this.life = initialLife;
18 | this.speed = speed;
19 | }
20 |
21 | /**
22 | * Returns normalized age from 1 to 0
23 | */
24 | float getAge() {
25 | return constrain(map(this.life, this.initialLife, 0, 0, 1), 0, 1);
26 | }
27 |
28 | /**
29 | * Return a new particle when branching at the same location
30 | */
31 | Particle branch() {
32 | return new Particle(this.location.x, this.location.y, this.offset * 2, this.life, this.speed);
33 | }
34 |
35 | /**
36 | * Update the particle, add noise and decrease life
37 | */
38 | void update() {
39 | float n = noise((this.offset + this.location.x) * 0.1, (this.offset + this.location.y) * 0.1);
40 |
41 | this.location.add(PVector.fromAngle(((n * 2) - 1) * this.getAge() * TWO_PI + HALF_PI).mult(-this.speed));
42 |
43 | this.life -= this.speed;
44 | }
45 |
46 | /**
47 | * Display transparent white dot
48 | */
49 | void display() {
50 | stroke(255, 50);
51 | strokeWeight(2);
52 | point(this.location.x, this.location.y);
53 | }
54 | }
55 |
56 | /**
57 | * Create random particles at the bottom of the screen
58 | * the spread controls the size of the trunk
59 | */
60 | List createRandomParticles(float spread, float margin) {
61 | List particles = new ArrayList();
62 |
63 | for (float x = (width - spread) / 2.0; x < (width + spread) / 2.0; x++) {
64 | float initialLife = random(100, 500);
65 | float randomSpeed = random(0.2, 2);
66 | particles.add(new Particle(x, height - margin, x / 2.0, initialLife, randomSpeed));
67 | }
68 |
69 | return particles;
70 | }
71 |
72 | List particles;
73 | float spread = 100;
74 | float margin = 50;
75 |
76 | void setup() {
77 | size(500, 500);
78 |
79 | particles = createRandomParticles(spread, margin);
80 |
81 | // Only need to draw the background once in setup
82 | background(#003344);
83 | }
84 |
85 | void draw() {
86 |
87 | // Loop through particles
88 | for (int i = particles.size() - 1; i >= 0; i--) {
89 | Particle particle = particles.get(i);
90 |
91 | particle.display();
92 | particle.update();
93 |
94 | float age = particle.getAge();
95 |
96 | // Delete if dead
97 | if (particle.life <= 0) {
98 | particles.remove(i);
99 | }
100 |
101 | // Branch condition
102 | if (random(2) < age * 0.02) {
103 | // Display an orange dot at that point
104 | stroke(#ff9900);
105 | strokeWeight(age * 10);
106 | point(particle.location.x, particle.location.y);
107 |
108 | // Add the new particle
109 | particles.add(particle.branch());
110 | }
111 | }
112 |
113 | // Stop when there's no more particles
114 | if (particles.size() == 0) {
115 | noLoop();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/2021/day_10/genuary_10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_10/genuary_10.gif
--------------------------------------------------------------------------------
/2021/day_11/day_11.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | let imageCounter = 0;
5 | let img;
6 | let time = 0;
7 | const changeTime = 2000;
8 |
9 | let particles = [];
10 |
11 | /**
12 | * Basic particle class
13 | */
14 | class Particle {
15 | /**
16 | * @speed is a vector
17 | */
18 | constructor(x, y, color, speed, initialLife) {
19 | this.location = createVector(x, y);
20 | this.color = color;
21 | this.speed = speed;
22 | this.initialLife = initialLife;
23 | this.life = this.initialLife;
24 | }
25 |
26 | /**
27 | * Return normalized age from 1 to 0
28 | */
29 | getAge() {
30 | return this.life / this.initialLife;
31 | }
32 |
33 | /**
34 | * Add speed to location with extra rotation
35 | * also decrease life
36 | */
37 | update() {
38 | const rotation = noise(this.location.x * 0.1, this.location.y * 0.1) * PI / 100 * (1 - this.getAge());
39 | this.location.add(this.speed.rotate(rotation));
40 |
41 | this.life -= 0.1;
42 | }
43 |
44 | display() {
45 | const age = this.getAge();
46 | stroke(red(this.color), green(this.color), blue(this.color), age * 20);
47 | strokeWeight(age * 10);
48 | point(this.location.x, this.location.y);
49 | }
50 | }
51 |
52 | /**
53 | * Create random particles from the image centered at 0
54 | */
55 | function createRandomParticlesFromImage(img) {
56 | img.loadPixels();
57 |
58 | for (let i = 0; i < 200; i++) {
59 | const rx = int(random(img.width));
60 | const ry = int(random(img.height));
61 |
62 | const diffX = rx - img.width / 2;
63 | const diffY = ry - img.height / 2;
64 |
65 | const speed = createVector(diffX, diffY).normalize().mult(random(0.01, 0.5));
66 | const loc = 4 * (rx + ry * img.width);
67 |
68 | const c = color(img.pixels[loc], img.pixels[loc + 1], img.pixels[loc + 2], 50);
69 |
70 | particles.push(new Particle(diffX, diffY, c, speed, random(100, 500)));
71 | }
72 | }
73 |
74 | /**
75 | * Get random image from : https://picsum.photos/
76 | */
77 | function randomImage(size) {
78 | // Add random parameter to load a different image each time
79 | return loadImage("https://picsum.photos/" + size + "?random=" + imageCounter++, (i) => {
80 | // Callback when loaded, replace the current image
81 | img = i;
82 | createRandomParticlesFromImage(img);
83 | });
84 | }
85 |
86 | function preload() {
87 | randomImage(100);
88 | }
89 |
90 | function setup() {
91 | createCanvas(500, 500);
92 |
93 | background("#003344");
94 | }
95 |
96 | function draw() {
97 | translate(width / 2, height / 2);
98 |
99 | // Display the particles
100 | for (let i = particles.length - 1; i >= 0; i--) {
101 | const particle = particles[i];
102 | particle.display();
103 | particle.update();
104 |
105 | const plx = particle.location.x;
106 | const ply = particle.location.y;
107 | if (particle.life <= 0 || plx > width / 2 || plx < -width / 2 || ply > height / 2 || ply < -height / 2) {
108 | particles.pop();
109 | }
110 | }
111 |
112 | // Display the image
113 | imageMode(CENTER);
114 | image(img, 0, 0);
115 |
116 | // Change image
117 | if (millis() - time > changeTime) {
118 | randomImage(100);
119 | time = millis();
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/2021/day_11/day_11.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | int imageCounter = 0;
7 | PImage img;
8 | int time = 0;
9 | int changeTime = 1000;
10 |
11 | List particles;
12 |
13 | /**
14 | * Basic particle class
15 | */
16 | class Particle {
17 | PVector location, speed;
18 | color col;
19 | float initialLife, life;
20 |
21 | /**
22 | * @speed is a vector
23 | */
24 | Particle(float x, float y, color col, PVector speed, float initialLife) {
25 | this.location = new PVector(x, y);
26 | this.col = col;
27 | this.speed = speed;
28 | this.initialLife = initialLife;
29 | this.life = this.initialLife;
30 | }
31 |
32 | /**
33 | * Return normalized age from 1 to 0
34 | */
35 | float getAge() {
36 | return this.life / this.initialLife;
37 | }
38 |
39 | /**
40 | * Add speed to location with extra rotation
41 | * also decrease life
42 | */
43 | void update() {
44 | float rotation = noise(this.location.x * 0.1, this.location.y * 0.1) * PI / 100.0 * (1 - this.getAge());
45 | this.location.add(this.speed.rotate(rotation));
46 |
47 | this.life -= 0.1;
48 | }
49 |
50 | void display() {
51 | float age = this.getAge();
52 | stroke(this.col, age * 20);
53 | strokeWeight(age * 10);
54 | point(this.location.x, this.location.y);
55 | }
56 | }
57 |
58 | /**
59 | * Create random particles from the image centered at 0
60 | */
61 | void createRandomParticlesFromImage(PImage img) {
62 | img.loadPixels();
63 |
64 | for (int i = 0; i < 200; i++) {
65 | int rx = int(random(img.width));
66 | int ry = int(random(img.height));
67 |
68 | float diffX = rx - img.width / 2;
69 | float diffY = ry - img.height / 2;
70 |
71 | PVector speed = new PVector(diffX, diffY).normalize().mult(random(0.01, 0.5));
72 | int loc = (rx + ry * img.width);
73 |
74 | color col = color(img.pixels[loc], 50);
75 |
76 | particles.add(new Particle(diffX, diffY, col, speed, random(100, 500)));
77 | }
78 | }
79 |
80 | /**
81 | * Get random image from : https://picsum.photos/
82 | */
83 | void randomImage(int size) {
84 | // Add random parameter to load a different image each time
85 | img = loadImage("https://picsum.photos/" + size + "?random=" + (imageCounter++) + ".png", "png");
86 | createRandomParticlesFromImage(img);
87 | }
88 |
89 | void setup() {
90 | size(500, 500);
91 |
92 | background(#003344);
93 |
94 | particles = new ArrayList();
95 |
96 | randomImage(100);
97 | }
98 |
99 | void draw() {
100 | translate(width / 2, height / 2);
101 |
102 | // Display the particles
103 | for (int i = particles.size() - 1; i >= 0; i--) {
104 | Particle particle = particles.get(i);
105 | particle.display();
106 | particle.update();
107 |
108 | float plx = particle.location.x;
109 | float ply = particle.location.y;
110 | if (particle.life <= 0 || plx > width / 2 || plx < -width / 2 || ply > height / 2 || ply < -height / 2) {
111 | particles.remove(i);
112 | }
113 | }
114 |
115 | // Display the image
116 | imageMode(CENTER);
117 | image(img, 0, 0);
118 |
119 | // Change image
120 | if (millis() - time > changeTime) {
121 | randomImage(100);
122 | time = millis();
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/2021/day_11/genuary_11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_11/genuary_11.gif
--------------------------------------------------------------------------------
/2021/day_12/day_12.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // Initialize activity with empty object
5 | let activity = {activity:"", type:""};
6 |
7 | /**
8 | * Get new activity as JSON from : http://www.boredapi.com/api/activity/
9 | * using xhr request
10 | */
11 | function getNewActivity() {
12 | const requestURL = "https://www.boredapi.com/api/activity/";
13 |
14 | const request = new XMLHttpRequest();
15 |
16 | request.open('GET', requestURL);
17 | request.responseType = 'json';
18 | request.send();
19 |
20 | request.onload = function() {
21 | activity = request.response;
22 | }
23 | }
24 |
25 | /**
26 | * Display a bone like shape where (x, y) is the origin
27 | * length and rotation
28 | */
29 | function vThickLine(x, y, d1, d2, length, rotation) {
30 | push();
31 | translate(x, y);
32 | rotate(rotation);
33 |
34 | // Upper half circle
35 | beginShape();
36 | const r2 = d2 / 2;
37 | for (let angle = PI; angle < TWO_PI; angle += 0.1) {
38 | vertex(cos(angle) * r2, sin(angle) * r2 - length);
39 | }
40 |
41 | // Down half circle
42 | const r1 = d1 / 2;
43 | for (let angle = 0; angle < PI; angle += 0.1) {
44 | vertex(cos(angle) * r1, sin(angle) * r1);
45 | }
46 |
47 | // Connect the two
48 | endShape(CLOSE);
49 |
50 | pop();
51 | }
52 |
53 | /**
54 | * Draw a bored person
55 | */
56 | function bored(x, y, vSize, factor) {
57 | push();
58 | translate(x, y);
59 |
60 | noStroke();
61 | fill(255, 200);
62 |
63 | // Too much consts and variables here, not optimized at all
64 | // but it's procedural anyway
65 | const headWidth = 60;
66 | const headHeight = 80;
67 |
68 | const headY = headHeight / 2 + (vSize - headHeight / 2) * factor;
69 |
70 | const armLength = vSize - headHeight / 2;
71 | const lowerDiameter = 30;
72 | const upperDiameter = 10;
73 | const handDiameter = upperDiameter / 2;
74 |
75 | const handX = (headWidth + upperDiameter) / 2;
76 |
77 | const armY = headY - headHeight / 2;
78 | const armOffset = sqrt((armLength ** 2) - (armY ** 2));
79 | const armRotation = acos(armOffset / armLength) - HALF_PI;
80 | const armX = handX + armOffset;
81 |
82 | // Shoulders
83 | const shoulderDiameter = lowerDiameter * 1.2;
84 | vThickLine(-headWidth / 2, -(headY - headHeight / 2), shoulderDiameter, lowerDiameter, armLength, -armRotation + PI);
85 | vThickLine(headWidth / 2, -(headY - headHeight / 2), shoulderDiameter, lowerDiameter, armLength, armRotation + PI);
86 |
87 | // Arms
88 | vThickLine(-armX, 0, lowerDiameter, upperDiameter, armLength, -armRotation);
89 | vThickLine(armX, 0, lowerDiameter, upperDiameter, armLength, armRotation);
90 |
91 | // Hands
92 | vThickLine(-handX, -armY, upperDiameter, handDiameter, headHeight / 1.5, 0);
93 | vThickLine(handX, -armY, upperDiameter, handDiameter, headHeight / 1.5, 0);
94 |
95 | // Head
96 | ellipse(0, -headY, headWidth, headHeight);
97 |
98 | // Eyes
99 | stroke("#ff9900");
100 | strokeWeight(10);
101 | point(-headWidth / 4, -headY);
102 | point(headWidth / 4, -headY);
103 |
104 | // Mouth
105 | strokeWeight(2);
106 | noFill();
107 | arc(0, -headY + headHeight / 5, 20, factor * 20, 0, PI);
108 |
109 | // eyes black dots
110 | stroke(0);
111 | strokeWeight(5);
112 | point(-headWidth / 4 + cos(armRotation * 4) * 2.5, -headY);
113 | point(headWidth / 4 + cos(armRotation * 4) * 2.5, -headY);
114 |
115 | pop();
116 | }
117 |
118 | let offset = 0;
119 | let time = 0;
120 | const incr = 0.03;
121 |
122 | function preload() {
123 | getNewActivity();
124 | }
125 |
126 | function setup() {
127 | createCanvas(500, 500);
128 | }
129 |
130 | function draw() {
131 | background("#003344");
132 |
133 | translate(width / 2, height / 2 + 50);
134 |
135 | bored(0, 0, 150, (sin(offset) + 1) / 2);
136 |
137 | // Text display
138 | textSize(8);
139 | stroke("#ff9900");
140 | strokeWeight(50);
141 | line(-200, 105, 200, 105);
142 |
143 | textAlign(CENTER, CENTER);
144 | fill(255);
145 | noStroke();
146 |
147 | textSize(15);
148 | textStyle(NORMAL);
149 | text("You're bored? Just : ", 0, -220);
150 |
151 | fill(255);
152 | textStyle(BOLD);
153 | text(activity.activity, 0, 100);
154 |
155 | textSize(8);
156 | text("type : " + activity.type, 0, 120);
157 |
158 | // Get new activity each cycle
159 | if (offset - time > TWO_PI) {
160 | getNewActivity();
161 | time = offset;
162 | }
163 |
164 | offset += incr;
165 | }
166 |
--------------------------------------------------------------------------------
/2021/day_12/day_12.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | JSONObject activity;
5 |
6 | void getNewActivity() {
7 | String requestURL = "https://www.boredapi.com/api/activity/";
8 | activity = loadJSONObject(requestURL);
9 | }
10 |
11 | void vThickLine(float x, float y, float d1, float d2, float length, float rotation) {
12 | push();
13 | translate(x, y);
14 | rotate(rotation);
15 |
16 | beginShape();
17 | float r2 = d2 / 2;
18 | for (float angle = PI; angle < TWO_PI; angle += 0.1) {
19 | vertex(cos(angle) * r2, sin(angle) * r2 - length);
20 | }
21 |
22 | float r1 = d1 / 2;
23 | for (float angle = 0; angle < PI; angle += 0.1) {
24 | vertex(cos(angle) * r1, sin(angle) * r1);
25 | }
26 | endShape(CLOSE);
27 |
28 | pop();
29 | }
30 |
31 | void bored(float x, float y, float vSize, float factor) {
32 | push();
33 | translate(x, y);
34 |
35 | noStroke();
36 | fill(255, 200);
37 |
38 | float headWidth = 60;
39 | float headHeight = 80;
40 |
41 | float headY = headHeight / 2 + (vSize - headHeight / 2) * factor;
42 |
43 | float armLength = vSize - headHeight / 2;
44 | float lowerDiameter = 30;
45 | float upperDiameter = 10;
46 | float handDiameter = upperDiameter / 2;
47 |
48 | float handX = (headWidth + upperDiameter) / 2;
49 |
50 | float armY = headY - headHeight / 2;
51 | float armOffset = sqrt(pow(armLength, 2) - pow(armY, 2));
52 | float armRotation = acos(armOffset / armLength) - HALF_PI;
53 | float armX = handX + armOffset;
54 |
55 | float shoulderDiameter = lowerDiameter * 1.2;
56 | vThickLine(-headWidth / 2, -(headY - headHeight / 2), shoulderDiameter, lowerDiameter, armLength, -armRotation + PI);
57 | vThickLine(headWidth / 2, -(headY - headHeight / 2), shoulderDiameter, lowerDiameter, armLength, armRotation + PI);
58 |
59 | // Arms
60 | vThickLine(-armX, 0, lowerDiameter, upperDiameter, armLength, -armRotation);
61 | vThickLine(armX, 0, lowerDiameter, upperDiameter, armLength, armRotation);
62 |
63 | // Hands
64 | vThickLine(-handX, -armY, upperDiameter, handDiameter, headHeight / 1.5, 0);
65 | vThickLine(handX, -armY, upperDiameter, handDiameter, headHeight / 1.5, 0);
66 |
67 | ellipse(0, -headY, headWidth, headHeight);
68 |
69 | stroke(#ff9900);
70 | strokeWeight(10);
71 | point(-headWidth / 4, -headY);
72 | point(headWidth / 4, -headY);
73 |
74 | strokeWeight(2);
75 | noFill();
76 | arc(0, -headY + headHeight / 5, 20, factor * 20, 0, PI);
77 |
78 | stroke(0);
79 | strokeWeight(5);
80 | point(-headWidth / 4 + cos(armRotation * 4) * 2.5, -headY);
81 | point(headWidth / 4 + cos(armRotation * 4) * 2.5, -headY);
82 |
83 | pop();
84 | }
85 |
86 | float offset = 0;
87 | float time = 0;
88 | float incr = 0.05;
89 |
90 | void setup() {
91 | size(500, 500);
92 |
93 | getNewActivity();
94 | }
95 |
96 | void draw() {
97 | background(#003344);
98 |
99 | translate(width / 2, height / 2 + 50);
100 |
101 | bored(0, 0, 150, (sin(offset) + 1) / 2);
102 |
103 | textSize(8);
104 | stroke(#ff9900);
105 | strokeWeight(50);
106 | line(-200, 105, 200, 105);
107 |
108 | textAlign(CENTER, CENTER);
109 | fill(255);
110 | noStroke();
111 |
112 | textSize(15);
113 | text("You're bored? Just : ", 0, -220);
114 |
115 | fill(255);
116 | text(activity.getString("activity"), 0, 100);
117 |
118 | textSize(8);
119 | text("type : " + activity.getString("type"), 0, 120);
120 |
121 | if (offset - time > TWO_PI) {
122 | getNewActivity();
123 | time = offset;
124 | n++;
125 | }
126 |
127 | offset += incr;
128 | }
129 |
--------------------------------------------------------------------------------
/2021/day_12/genuary_12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_12/genuary_12.gif
--------------------------------------------------------------------------------
/2021/day_13/day_13.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | const CANVAS_SIZE = 500;
5 |
6 | const circles = [];
7 |
8 | function pickRandomPointInCircle(x, y, diameter) {
9 | const randomAngle = random(TWO_PI);
10 | const randomRadius = random(0, diameter / 2);
11 | return createVector(cos(randomAngle) * randomRadius, sin(randomAngle) * randomRadius);
12 | }
13 |
14 | function recursiveCircles(x, y, diameter, depth) {
15 | if (depth == 0) return;
16 |
17 | stroke(255, 50);
18 | noFill();
19 | strokeWeight(2);
20 | circle(x, y, diameter);
21 |
22 | for (let i = 0; i < depth; i++) {
23 | const nextPoint = pickRandomPointInCircle(x, y, diameter);
24 |
25 | stroke(255, 153, 0, 100);
26 | strokeWeight(5);
27 | point(nextPoint.x, nextPoint.y);
28 | strokeWeight(1);
29 | line(x, y, nextPoint.x, nextPoint.y);
30 |
31 | const distanceFromCenter = dist(x, y, nextPoint.x, nextPoint.y);
32 |
33 | const nextDiameter = diameter - (2 * distanceFromCenter);
34 |
35 | recursiveCircles(nextPoint.x, nextPoint.y, nextDiameter, depth - 1);
36 | }
37 | }
38 |
39 | const time = 0;
40 |
41 | function setup() {
42 | createCanvas(CANVAS_SIZE, CANVAS_SIZE);
43 |
44 | background("#003344");
45 | }
46 |
47 | function draw() {
48 |
49 |
50 | translate(width / 2, height / 2);
51 |
52 | recursiveCircles(0, 0, 300, 3);
53 |
54 | noLoop();
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/2021/day_13/day_13.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Helper function to get a random point inside a circle
6 | */
7 | PVector pickRandomPointInCircle(float x, float y, float diameter) {
8 | float randomAngle = random(TWO_PI);
9 | float randomRadius = random(0, diameter / 2);
10 | return new PVector(cos(randomAngle) * randomRadius, sin(randomAngle) * randomRadius);
11 | }
12 |
13 | /**
14 | * Recursive function that draws nested circles
15 | * no repetitions here since the function draws itself
16 | */
17 | void recursiveCircles(float x, float y, float diameter, int depth) {
18 | // End condition
19 | if (depth == 0) return;
20 |
21 | stroke(255, 80);
22 | noFill();
23 | strokeWeight(2);
24 | circle(x, y, diameter);
25 |
26 | // Pick depth random points
27 | for (int i = 0; i < depth; i++) {
28 | PVector nextPoint = pickRandomPointInCircle(x, y, diameter);
29 |
30 | stroke(255, 153, 0, 200);
31 | strokeWeight(5);
32 | point(nextPoint.x, nextPoint.y);
33 | strokeWeight(1);
34 | line(x, y, nextPoint.x, nextPoint.y);
35 |
36 | // Compute next diameter in order to stay inside the previous circle
37 | float distanceFromCenter = dist(x, y, nextPoint.x, nextPoint.y);
38 | float nextDiameter = diameter - (2 * distanceFromCenter);
39 |
40 | // Recursive call
41 | recursiveCircles(nextPoint.x, nextPoint.y, nextDiameter, depth - 1);
42 | }
43 | }
44 |
45 | final int timeChange = 500;
46 | int time = 0;
47 |
48 | /**
49 | * Reset background and draw a new configuration
50 | */
51 | void updateView() {
52 | background(#003344);
53 |
54 | translate(width / 2, height / 2);
55 | recursiveCircles(0, 0, 350, 3);
56 | }
57 |
58 | void setup() {
59 | size(500, 500);
60 |
61 | updateView();
62 | }
63 |
64 | void draw() {
65 | // Change the view when it's time
66 | if (millis() - time > timeChange) {
67 | updateView();
68 | // Reset time
69 | time = millis();
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/2021/day_13/genuary_13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_13/genuary_13.gif
--------------------------------------------------------------------------------
/2021/day_14/genuary_14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_14/genuary_14.gif
--------------------------------------------------------------------------------
/2021/day_15/day_15.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Draw a layer with a hole and stroke
6 | */
7 | function layer(x, y, z, size, rotationY, rotationZ, fillColor) {
8 | // Transform
9 | push();
10 | translate(x, y, z);
11 | rotateZ(QUARTER_PI + rotationY);
12 | rotateY(rotationZ);
13 |
14 | // Base
15 | rectMode(CENTER);
16 | fill(fillColor);
17 | stroke("#003344");
18 | strokeWeight(20);
19 | plane(size, size);
20 |
21 | // Hole
22 | fill("#003344");
23 | noStroke();
24 | plane(size * 0.7, size * 0.7);
25 |
26 | pop();
27 | }
28 |
29 | /**
30 | * Easing functions
31 | * From : https://easings.net/
32 | */
33 | function easeInSine(x) {
34 | return 1 - cos((x * PI) / 2);
35 | }
36 |
37 | function easeInOutCubic(x) {
38 | return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
39 | }
40 |
41 | let offset = 0;
42 |
43 | let fromYellow, toYellow;
44 | let fromBlue, toBlue;
45 |
46 | function setup() {
47 | createCanvas(500, 500, WEBGL);
48 |
49 | // WEBGL attributes
50 | setAttributes('antialias', true);
51 | setAttributes('depth', false);
52 |
53 | // Define colors
54 | fromYellow = color("#ff7300");
55 | toYellow = color("#ff9900");
56 |
57 | fromBlue = color("#84d0ef");
58 | toBlue = color("#2790cf");
59 | }
60 |
61 | function draw() {
62 | background("#003344");
63 |
64 | const nLayers = 8;
65 | const gap = easeInSine(cos(offset)) * 20 + 20;
66 | const rotationY = easeInSine(sin(offset)) * PI;
67 | const startX = -(gap * (nLayers - 1)) / 2;
68 |
69 | for (let i = 0; i < nLayers; i++) {
70 | const x = startX + (gap * i);
71 | const rotationZ = cos(offset) * PI + i * (sin(offset) * cos(offset) * 0.5);
72 |
73 | const lerp = easeInOutCubic(abs(sin(offset)));
74 | const from = lerpColor(fromBlue, fromYellow, lerp);
75 | const to = lerpColor(toBlue, toYellow, lerp);
76 | const fillColor = lerpColor(from, to, i / nLayers);
77 |
78 | const size = map(i, 0, nLayers, 150, 20);
79 |
80 | layer(0, -x - 40, 0, size, rotationY, rotationZ, fillColor);
81 | }
82 |
83 | offset += 0.01;
84 | }
85 |
--------------------------------------------------------------------------------
/2021/day_15/day_15.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Draw a plane
6 | */
7 | void plane(float size) {
8 | float h = size / 2;
9 | beginShape();
10 | vertex(-h, -h, 0);
11 | vertex(h, -h, 0);
12 | vertex(h, h, 0);
13 | vertex(-h, h);
14 | endShape(CLOSE);
15 | }
16 |
17 | /**
18 | * Draw a layer with a hole and stroke
19 | */
20 | void layer(float x, float y, float z, float size, float rotationY, float rotationZ, color fillColor) {
21 | // Transform
22 | push();
23 | translate(x, y, z);
24 | rotateZ(QUARTER_PI + rotationY);
25 | rotateY(rotationZ);
26 |
27 | // Plane stroke
28 | noFill();
29 | stroke(#003344);
30 | strokeWeight(20);
31 | plane(size);
32 |
33 | // Base
34 | rectMode(CENTER);
35 | fill(fillColor);
36 | noStroke();
37 | plane(size);
38 |
39 |
40 |
41 | // Hole
42 | fill(#003344);
43 | noStroke();
44 | plane(size * 0.7);
45 |
46 | pop();
47 | }
48 |
49 | /**
50 | * Easing functions
51 | * From : https://easings.net/
52 | */
53 | float easeInSine(float x) {
54 | return 1 - cos((x * PI) / 2.0);
55 | }
56 |
57 | float easeInOutCubic(float x) {
58 | return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
59 | }
60 |
61 | float offset = 0;
62 |
63 | color fromYellow, toYellow;
64 | color fromBlue, toBlue;
65 |
66 | void settings() {
67 | System.setProperty("jogl.disable.openglcore", "true");
68 | size(500, 500, P3D);
69 | }
70 |
71 | void setup() {
72 | hint(DISABLE_DEPTH_TEST);
73 | hint(DISABLE_OPTIMIZED_STROKE);
74 |
75 | // Define colors
76 | fromYellow = color(#ff7300);
77 | toYellow = color(#ff9900);
78 |
79 | fromBlue = color(#84d0ef);
80 | toBlue = color(#2790cf);
81 | }
82 |
83 | void draw() {
84 | background(#003344);
85 |
86 | int nLayers = 8;
87 | float gap = easeInSine(cos(offset)) * 20 + 20;
88 | float rotationY = easeInSine(sin(offset)) * PI;
89 | float startX = -(gap * (nLayers - 1)) / 2;
90 |
91 | for (int i = 0; i < nLayers; i++) {
92 | float x = startX + (gap * i);
93 | float rotationZ = cos(offset) * PI + i * (sin(offset) * cos(offset) * 0.5);
94 |
95 | float lerp = easeInOutCubic(abs(sin(offset)));
96 | color from = lerpColor(fromBlue, fromYellow, lerp);
97 | color to = lerpColor(toBlue, toYellow, lerp);
98 | color fillColor = lerpColor(from, to, i / nLayers);
99 |
100 | float size = map(i, 0, nLayers, 150, 20);
101 |
102 | layer(width / 2, height / 2 - x - 40, 0, size, rotationY, rotationZ, fillColor);
103 | }
104 |
105 | offset += 0.01;
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/2021/day_15/genuary_15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_15/genuary_15.gif
--------------------------------------------------------------------------------
/2021/day_16/day_16.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | const blue = "#003344";
5 | const yellow = "#ff9900";
6 |
7 | function easeInOutQuint(x) {
8 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
9 | }
10 |
11 | function easeOffset(offset) {
12 | return easeInOutQuint((cos(offset) + 1) / 2) - easeInOutQuint((sin(offset / 2)));
13 | }
14 |
15 | function circlePattern(x, y, size, offset) {
16 | push();
17 | translate(x, y);
18 |
19 | const osc = easeOffset(offset);
20 | const oscHP = easeOffset(offset + HALF_PI);
21 | const oscP = easeOffset(offset + PI);
22 |
23 | rotate(osc * HALF_PI);
24 |
25 | const half = size / 2;
26 |
27 | strokeWeight(5);
28 |
29 | fill(255);
30 | stroke(yellow);
31 | circle(0, 0, size);
32 |
33 | fill(blue);
34 | stroke(blue);
35 | circle(half * osc, 0, size * 0.75);
36 | circle(-half * osc, 0, size * 0.75);
37 |
38 | circle(0, half, half * osc);
39 | circle(0, -half, half * osc);
40 |
41 | strokeWeight(10);
42 |
43 | fill(yellow);
44 | circle(half, 0, half);
45 | circle(-half, 0, half);
46 |
47 | circle(0, 0, half * 0.75 * oscP);
48 |
49 | fill(blue);
50 | stroke(yellow);
51 | circle(half * oscHP, (half / 2) * oscHP, half / 2);
52 | circle(-half * oscHP, (-half / 2) * oscHP, half / 2);
53 |
54 | circle(half, 0, (half / 4) * oscHP);
55 | circle(-half, 0, (half / 4) * oscHP);
56 | pop();
57 | }
58 |
59 | let offset = 0;
60 |
61 | function setup() {
62 | createCanvas(500, 500);
63 | }
64 |
65 | function draw() {
66 | background(blue);
67 |
68 | circlePattern(width / 2, height / 2, 250, offset);
69 |
70 | offset += 0.05;
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/2021/day_16/day_16.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | color blue;
5 | color yellow;
6 |
7 | float easeInOutQuint(float x) {
8 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
9 | }
10 |
11 | float easeOffset(float offset) {
12 | return easeInOutQuint((cos(offset) + 1) / 2) - easeInOutQuint((sin(offset / 2)));
13 | }
14 |
15 | void circlePattern(float x, float y, float size, float offset) {
16 | push();
17 | translate(x, y);
18 |
19 | float osc = easeOffset(offset);
20 | float oscHP = easeOffset(offset + HALF_PI);
21 | float oscP = easeOffset(offset + PI);
22 |
23 | rotate(osc * HALF_PI);
24 |
25 | float half = size / 2;
26 |
27 | strokeWeight(5);
28 |
29 | fill(255);
30 | stroke(yellow);
31 | circle(0, 0, size);
32 |
33 | fill(blue);
34 | stroke(blue);
35 | circle(half * osc, 0, size * 0.75);
36 | circle(-half * osc, 0, size * 0.75);
37 |
38 | circle(0, half, half * osc);
39 | circle(0, -half, half * osc);
40 |
41 | strokeWeight(10);
42 |
43 | fill(yellow);
44 | circle(half, 0, half);
45 | circle(-half, 0, half);
46 |
47 | circle(0, 0, half * 0.75 * oscP);
48 |
49 | fill(blue);
50 | stroke(yellow);
51 | circle(half * oscHP, (half / 2) * oscHP, half / 2);
52 | circle(-half * oscHP, (-half / 2) * oscHP, half / 2);
53 |
54 | circle(half, 0, (half / 4) * oscHP);
55 | circle(-half, 0, (half / 4) * oscHP);
56 | pop();
57 | }
58 |
59 | float offset = 0;
60 |
61 | void setup() {
62 | size(500, 500);
63 |
64 | blue = color(#003344);
65 | yellow = color(#ff9900);
66 | }
67 |
68 | void draw() {
69 | background(blue);
70 |
71 | circlePattern(width / 2, height / 2, 250, offset);
72 |
73 | offset += 0.04;
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/2021/day_16/genuary_16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_16/genuary_16.gif
--------------------------------------------------------------------------------
/2021/day_17/day_17.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | let yellow, orange, pink;
5 |
6 | let offset = 0;
7 |
8 | function lines(x, y, size, nLines, offset, fromColor, toColor) {
9 | const slice = TWO_PI / nLines;
10 | const radius = size / 2;
11 |
12 | push();
13 | translate(x, y);
14 |
15 | for (let i = 0; i < nLines; i++) {
16 | randomSeed(i * 500);
17 |
18 | const angle = i * slice;
19 | const rotMultiplier = sin(angle / 2) + 1;
20 | const rotation = angle * rotMultiplier + offset;
21 | const color = lerpColor(fromColor, toColor, random(1));
22 |
23 | stroke(color);
24 | strokeWeight(map(sin(rotation * 2), -1, 1, size / 20, size / 5));
25 |
26 | push();
27 | translate(cos(angle) * radius, sin(angle) * radius);
28 | rotate(rotation);
29 | line(0, 0, size / 4, 0);
30 | pop();
31 | }
32 |
33 | pop();
34 | }
35 |
36 | function setup() {
37 | createCanvas(500, 500);
38 |
39 | yellow = color("#ff9900");
40 | orange = color("#ff5500");
41 | pink = color("#ff7c67");
42 | }
43 |
44 | function draw() {
45 | background("#003344");
46 |
47 | translate(width / 2, height / 2);
48 |
49 | noStroke();
50 | fill(pink);
51 | circle(0, 0, 120 + sin(offset) * 20);
52 |
53 | fill(orange);
54 | circle(0, 0, 100 + sin(offset + QUARTER_PI) * 10);
55 |
56 | fill(yellow);
57 | circle(0, 0, 50 + sin(offset) * 20);
58 |
59 |
60 | lines(0, 0, 250, 50, offset, yellow, orange);
61 | lines(0, 0, 150, 25, offset, orange, pink);
62 |
63 | offset += 0.03;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/2021/day_17/day_17.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | color yellow, orange, pink;
5 |
6 | float offset = 0;
7 |
8 | void lines(float x, float y, float size, int nLines, float offset, color fromColor, color toColor) {
9 | float slice = TWO_PI / (float) nLines;
10 | float radius = size / 2;
11 |
12 | push();
13 | translate(x, y);
14 |
15 | for (int i = 0; i < nLines; i++) {
16 | randomSeed(i * 1000);
17 |
18 | float angle = i * slice;
19 | float rotMultiplier = sin(angle / 2) + 1;
20 | float rotation = angle * rotMultiplier + offset;
21 | color col = lerpColor(fromColor, toColor, random(1));
22 |
23 | stroke(col);
24 | strokeWeight(map(sin(rotation * 2), -1, 1, size / 20, size / 5));
25 |
26 | push();
27 | translate(cos(angle) * radius, sin(angle) * radius);
28 | rotate(rotation);
29 | line(0, 0, size / 4, 0);
30 | pop();
31 | }
32 |
33 | pop();
34 | }
35 |
36 | void setup() {
37 | size(500, 500);
38 |
39 | yellow = color(#ff9900);
40 | orange = color(#ff5500);
41 | pink = color(#ff7c67);
42 | }
43 |
44 | void draw() {
45 | background(#003344);
46 |
47 | translate(width / 2, height / 2);
48 |
49 | noStroke();
50 | fill(pink);
51 | circle(0, 0, 120 + sin(offset) * 20);
52 |
53 | fill(orange);
54 | circle(0, 0, 100 + sin(offset + QUARTER_PI) * 10);
55 |
56 | fill(yellow);
57 | circle(0, 0, 50 + sin(offset) * 20);
58 |
59 |
60 | lines(0, 0, 250, 50, offset, yellow, orange);
61 | lines(0, 0, 150, 25, offset, orange, pink);
62 |
63 | offset += 0.03;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/2021/day_17/genuary_17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_17/genuary_17.gif
--------------------------------------------------------------------------------
/2021/day_18/day_18.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Grid class holding cells
6 | */
7 | class Grid {
8 | constructor(cols, rows, margin) {
9 | // 2D boolean array
10 | this.cells = [];
11 |
12 | this.cols = cols;
13 | this.rows = rows;
14 |
15 | // Fill the grid
16 | for (let i = 0; i < cols; i++) {
17 | this.cells[i] = [];
18 | for (let j = 0; j < rows; j++) {
19 | this.cells[i][j] = true;
20 | }
21 | }
22 |
23 | this.margin = margin;
24 |
25 | // The size of a cell
26 | this.cellSize = (width - 2 * margin) / cols;
27 | }
28 |
29 | display() {
30 | noStroke();
31 |
32 | // Loop through every cells
33 | for (let i = 0; i < this.cols; i++) {
34 | const x = this.margin + i * this.cellSize;
35 |
36 | for (let j = 0; j < this.rows; j++) {
37 | // Test if the cell is activated
38 | if (this.cells[i][j]) {
39 | fill(255, 153, 0, 100);
40 | } else {
41 | fill(0, 51, 68, 20);
42 | }
43 |
44 | const y = this.margin + j * this.cellSize;
45 | circle(x, y, this.cellSize * 1.5);
46 | }
47 | }
48 | }
49 | }
50 |
51 | /**
52 | * Basic particle class, location direction and speed
53 | */
54 | class Particle {
55 | constructor(x, y, angle, speed) {
56 | this.location = createVector(x, y);
57 | this.direction = p5.Vector.fromAngle(angle);
58 | this.speed = speed;
59 |
60 | // Assign random color (blue)
61 | this.color = lerpColor(color("#0066ff"), color("#0047b3"), random(1));
62 |
63 | // Change the noise seed so that the movement is different each time
64 | noiseSeed(random(10000));
65 | }
66 |
67 | /**
68 | * Return the next location of the particle based on the direction and speed
69 | */
70 | getNextLocation() {
71 | return p5.Vector.add(this.location, p5.Vector.mult(this.direction, this.speed));
72 | }
73 |
74 | /**
75 | * Return the heading angle of the particle
76 | */
77 | getHeading() {
78 | return this.direction.heading();
79 | }
80 |
81 | /**
82 | * Update the particle's location and direction
83 | */
84 | update() {
85 | this.location = this.getNextLocation();
86 |
87 | const angleRange = this.speed / 10;
88 | this.direction.rotate(map(noise(this.location.x / 100, this.location.y / 100), 0, 1, -angleRange, angleRange));
89 |
90 | this.speed -= 0.06;
91 | }
92 |
93 | /**
94 | * Display the particle as line segments
95 | */
96 | display() {
97 | const nextLocation = this.getNextLocation();
98 | stroke(this.color);
99 | strokeWeight(this.speed);
100 | line(this.location.x, this.location.y, nextLocation.x, nextLocation.y);
101 | }
102 | }
103 |
104 | let particles;
105 | let grid;
106 |
107 | /**
108 | * Initialize a new simulation
109 | */
110 | function initialize() {
111 | background("#003344");
112 |
113 | particles = [];
114 | grid = new Grid(50, 50, 50);
115 |
116 | // Create new particles
117 | for (let i = 0; i < int(random(200, 300)); i++) {
118 | const randomAngle = random(0, TWO_PI);
119 | const randomSpeed = random(1, 10);
120 | particles.push(new Particle(width / 2, height / 2, randomAngle, randomSpeed));
121 | }
122 | }
123 |
124 | function setup() {
125 | createCanvas(500, 500);
126 |
127 | initialize();
128 | }
129 |
130 | function draw() {
131 |
132 | grid.display();
133 |
134 | // For every particle (in reverse order to delete it)
135 | for (let i = particles.length - 1; i >= 0; i--) {
136 | const particle = particles[i];
137 |
138 | particle.display();
139 | particle.update();
140 |
141 | // Compute grid cell index under the particle
142 | const col = constrain(round((particle.location.x - grid.margin) / grid.cellSize), 0, grid.cols - 1);
143 | const row = constrain(round((particle.location.y - grid.margin) / grid.cellSize), 0, grid.rows - 1);
144 |
145 | // Invert it
146 | grid.cells[col][row] = !grid.cells[col][row];
147 |
148 | // Delete if stopped or if out of bounds
149 | if (particle.speed <= 0 || particle.location.x < grid.margin || particle.location.x > width - grid.margin || particle.location.y < grid.margin || particle.location.y > height - grid.margin) {
150 | particles.pop();
151 | }
152 | }
153 |
154 | // If there's no more particles, start again
155 | if (particles.length == 0) {
156 | initialize();
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/2021/day_18/day_18.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * Grid class holding cells
8 | */
9 | class Grid {
10 | int cols, rows;
11 | float margin, cellSize;
12 |
13 | boolean[][] cells;
14 |
15 | Grid(int cols, int rows, float margin) {
16 | // 2D boolean array
17 | this.cells = new boolean[cols][rows];
18 |
19 | this.cols = cols;
20 | this.rows = rows;
21 |
22 | // Fill the grid
23 | for (int i = 0; i < cols; i++) {
24 | for (int j = 0; j < rows; j++) {
25 | this.cells[i][j] = true;
26 | }
27 | }
28 |
29 | this.margin = margin;
30 |
31 | // The size of a cell
32 | this.cellSize = (width - 2 * margin) / (float) cols;
33 | }
34 |
35 | void display() {
36 | noStroke();
37 |
38 | // Loop through every cells
39 | for (int i = 0; i < this.cols; i++) {
40 | float x = this.margin + i * this.cellSize;
41 |
42 | for (int j = 0; j < this.rows; j++) {
43 | // Test if the cell is activated
44 | if (this.cells[i][j]) {
45 | fill(255, 153, 0, 100);
46 | } else {
47 | fill(0, 51, 68, 20);
48 | }
49 |
50 | float y = this.margin + j * this.cellSize;
51 | circle(x, y, this.cellSize * 1.5);
52 | }
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * Basic particle class, location direction and speed
59 | */
60 | class Particle {
61 | PVector location, direction;
62 | float speed;
63 | color col;
64 |
65 | Particle(float x, float y, float angle, float speed) {
66 | this.location = new PVector(x, y);
67 | this.direction = PVector.fromAngle(angle);
68 | this.speed = speed;
69 |
70 | // Assign random color (blue)
71 | this.col = lerpColor(color(#0066ff), color(#0047b3), random(1));
72 |
73 | // Change the noise seed so that the movement is different each time
74 | noiseSeed((long)random(10000));
75 | }
76 |
77 | /**
78 | * Return the next location of the particle based on the direction and speed
79 | */
80 | PVector getNextLocation() {
81 | return PVector.add(this.location, PVector.mult(this.direction, this.speed));
82 | }
83 |
84 | /**
85 | * Return the heading angle of the particle
86 | */
87 | float getHeading() {
88 | return this.direction.heading();
89 | }
90 |
91 | /**
92 | * Update the particle's location and direction
93 | */
94 | void update() {
95 | this.location = this.getNextLocation();
96 |
97 | float angleRange = this.speed / 10.0;
98 | this.direction.rotate(map(noise(this.location.x / 100, this.location.y / 100), 0, 1, -angleRange, angleRange));
99 |
100 | this.speed -= 0.06;
101 | }
102 |
103 | /**
104 | * Display the particle as line segments
105 | */
106 | void display() {
107 | PVector nextLocation = this.getNextLocation();
108 | stroke(this.col);
109 | strokeWeight(this.speed);
110 | line(this.location.x, this.location.y, nextLocation.x, nextLocation.y);
111 | }
112 | }
113 |
114 | List particles;
115 | Grid grid;
116 |
117 | /**
118 | * Initialize a new simulation
119 | */
120 | void initialize() {
121 | background(#003344);
122 |
123 | particles = new ArrayList();
124 | grid = new Grid(50, 50, 50);
125 |
126 | // Create new particles
127 | for (int i = 0; i < int(random(200, 300)); i++) {
128 | float randomAngle = random(0, TWO_PI);
129 | float randomSpeed = random(1, 10);
130 | particles.add(new Particle(width / 2, height / 2, randomAngle, randomSpeed));
131 | }
132 | }
133 |
134 | void setup() {
135 | size(500, 500);
136 |
137 | initialize();
138 | }
139 |
140 | void draw() {
141 |
142 | grid.display();
143 |
144 | // For every particle (in reverse order to delete it)
145 | for (int i = particles.size() - 1; i >= 0; i--) {
146 | Particle particle = particles.get(i);
147 |
148 | particle.display();
149 | particle.update();
150 |
151 | // Compute grid cell index under the particle
152 | int col = constrain(round((particle.location.x - grid.margin) / grid.cellSize), 0, grid.cols - 1);
153 | int row = constrain(round((particle.location.y - grid.margin) / grid.cellSize), 0, grid.rows - 1);
154 |
155 | // Invert it
156 | grid.cells[col][row] = !grid.cells[col][row];
157 |
158 | PVector nextLocation = particle.getNextLocation();
159 |
160 | // Delete if stopped or if out of bounds
161 | if (particle.speed <= 0 ||
162 | nextLocation.x < grid.margin ||
163 | nextLocation.x > width - grid.margin ||
164 | nextLocation.y < grid.margin ||
165 | nextLocation.y > height - grid.margin) {
166 | particles.remove(i);
167 | }
168 | }
169 |
170 | // If there's no more particles, start again
171 | if (particles.size() == 0) {
172 | initialize();
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/2021/day_18/genuary_18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_18/genuary_18.gif
--------------------------------------------------------------------------------
/2021/day_19/day_19.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | class Arrow {
5 | constructor(originX, originY, nSegments, height) {
6 | this.origin = createVector(originX, originY);
7 |
8 | this.nSegments = nSegments;
9 | this.segmentSize = height / nSegments;
10 |
11 | this.seed = random(10000);
12 | }
13 |
14 | getAngleAtPoint(ratio, offset) {
15 | return ((ratio - 0.5) * 2) * noise(ratio * 5 + this.seed + offset) * HALF_PI;
16 | }
17 |
18 | display(offset) {
19 | let currentY = this.origin.y;
20 |
21 | noFill();
22 |
23 | for (let i = 0; i < this.nSegments; i++) {
24 | const yRatio = map(i, 0, this.nSegments, 0, 1);
25 | const yRatioNext = map(i + 1, 0, this.nSegments, 0, 1);
26 |
27 | const nextY = currentY + this.segmentSize * yRatio * ((sin(offset + this.seed) + 3.5) / 2);
28 |
29 | const angleOffset = ((noise(this.seed + offset / 5) - 0.5) * 2) * PI;
30 | const angle = this.getAngleAtPoint(yRatio, offset) + angleOffset;
31 | const angleNext = this.getAngleAtPoint(yRatioNext, offset) + angleOffset;
32 |
33 | const length = 10 + (yRatio + 0.2) * 200 * noise(yRatio * 10 + this.seed + offset / 2);
34 | const handleLength = length * yRatio;
35 | const handleLengthNext = length * yRatioNext;
36 |
37 | const ctrlX1 = this.origin.x + cos(angle) * handleLength;
38 | const ctrlY1 = currentY + sin(angle) * handleLength;
39 |
40 | const ctrlX2 = this.origin.x + cos(PI + angleNext) * handleLengthNext;
41 | const ctrlY2 = nextY + sin(PI + angleNext) * handleLengthNext;
42 |
43 | stroke(255);
44 | strokeWeight(1);
45 | bezier(this.origin.x, currentY, ctrlX1, ctrlY1, ctrlX2, ctrlY2, this.origin.x, nextY);
46 |
47 | // Display the arrow
48 | if (i == this.nSegments - 1) {
49 | stroke("#ff9900");
50 | strokeWeight(3);
51 |
52 | const arrowSize = noise(this.seed) * 30;
53 |
54 | line(this.origin.x, nextY, this.origin.x + cos(PI + QUARTER_PI + angleNext) * arrowSize, nextY + sin(PI + QUARTER_PI + angleNext) * arrowSize);
55 | line(this.origin.x, nextY, this.origin.x + cos(PI - QUARTER_PI + angleNext) * arrowSize, nextY + sin(PI - QUARTER_PI + angleNext) * arrowSize);
56 | }
57 |
58 | currentY = nextY;
59 | }
60 | }
61 | }
62 |
63 | const nArrows = 15;
64 | const gap = 20;
65 | const arrows = [];
66 | let offset = 0;
67 |
68 | function setup() {
69 | createCanvas(500, 500);
70 |
71 | const startX = (width - gap * nArrows) / 2;
72 | for (let i = 0; i < nArrows; i++) {
73 | arrows.push(new Arrow(startX + i * gap, 50, 10, random(250, 400)));
74 | }
75 | }
76 |
77 | function draw() {
78 | background("#003344");
79 |
80 | for (const arrow of arrows) {
81 | arrow.display(offset);
82 | }
83 |
84 | offset += 0.05;
85 | }
86 |
--------------------------------------------------------------------------------
/2021/day_19/day_19.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | class Arrow {
7 | PVector origin;
8 | int nSegments, seed;
9 | float segmentSize;
10 |
11 | Arrow(float originX, float originY, int nSegments, float size) {
12 | this.origin = new PVector(originX, originY);
13 |
14 | this.nSegments = nSegments;
15 | this.segmentSize = size / nSegments;
16 |
17 | this.seed = int(random(10000));
18 | }
19 |
20 | float getAngleAtPoint(float ratio, float offset) {
21 | return ((ratio - 0.5) * 2) * noise(ratio * 5 + this.seed + offset) * HALF_PI;
22 | }
23 |
24 | void display(float offset) {
25 | float currentY = this.origin.y;
26 |
27 | noFill();
28 |
29 | for (int i = 0; i < this.nSegments; i++) {
30 | float yRatio = map(i, 0, this.nSegments, 0, 1);
31 | float yRatioNext = map(i + 1, 0, this.nSegments, 0, 1);
32 |
33 | float nextY = currentY + this.segmentSize * yRatio * ((sin(offset + this.seed) + 3.5) / 2);
34 |
35 | float angleOffset = ((noise(this.seed + offset / 5) - 0.5) * 2) * PI;
36 | float angle = this.getAngleAtPoint(yRatio, offset) + angleOffset;
37 | float angleNext = this.getAngleAtPoint(yRatioNext, offset) + angleOffset;
38 |
39 | float length = 10 + (yRatio + 0.2) * 200 * noise(yRatio * 10 + this.seed + offset / 2);
40 | float handleLength = length * yRatio;
41 | float handleLengthNext = length * yRatioNext;
42 |
43 | float ctrlX1 = this.origin.x + cos(angle) * handleLength;
44 | float ctrlY1 = currentY + sin(angle) * handleLength;
45 |
46 | float ctrlX2 = this.origin.x + cos(PI + angleNext) * handleLengthNext;
47 | float ctrlY2 = nextY + sin(PI + angleNext) * handleLengthNext;
48 |
49 | stroke(255);
50 | strokeWeight(1);
51 | bezier(this.origin.x, currentY, ctrlX1, ctrlY1, ctrlX2, ctrlY2, this.origin.x, nextY);
52 |
53 | // Display the arrow
54 | if (i == this.nSegments - 1) {
55 | stroke(#ff9900);
56 | strokeWeight(3);
57 |
58 | float arrowSize = noise(this.seed) * 30;
59 |
60 | line(this.origin.x, nextY, this.origin.x + cos(PI + QUARTER_PI + angleNext) * arrowSize, nextY + sin(PI + QUARTER_PI + angleNext) * arrowSize);
61 | line(this.origin.x, nextY, this.origin.x + cos(PI - QUARTER_PI + angleNext) * arrowSize, nextY + sin(PI - QUARTER_PI + angleNext) * arrowSize);
62 | }
63 |
64 | currentY = nextY;
65 | }
66 | }
67 | }
68 |
69 | int nArrows = 15;
70 | int gap = 20;
71 | List arrows = new ArrayList();
72 | float offset = 0;
73 |
74 | void setup() {
75 | size(500, 500);
76 |
77 | float startX = (width - gap * nArrows) / 2;
78 | for (int i = 0; i < nArrows; i++) {
79 | arrows.add(new Arrow(startX + i * gap, 50, 10, random(250, 400)));
80 | }
81 | }
82 |
83 | void draw() {
84 | background(#003344);
85 |
86 | for (Arrow arrow : arrows) {
87 | arrow.display(offset);
88 | }
89 |
90 | offset += 0.05;
91 | }
92 |
--------------------------------------------------------------------------------
/2021/day_19/genuary_19.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_19/genuary_19.gif
--------------------------------------------------------------------------------
/2021/day_2/day_2.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Fill a cell with diagonal cross hatches
6 | */
7 | function fillCell(x, y, cellSize, step) {
8 | let offset = step;
9 |
10 | while (offset <= cellSize * 2 - step) {
11 | if (offset < cellSize) {
12 | line(x + offset, y, x, y + offset);
13 | } else {
14 | line(x + cellSize, y + offset - cellSize, x + offset - cellSize, y + cellSize);
15 | }
16 |
17 | offset += step;
18 | }
19 | }
20 |
21 | /**
22 | * Modulo with non negative number (for indices)
23 | * See : https://stackoverflow.com/questions/1082917/mod-of-negative-number-is-melting-my-brain
24 | */
25 | function mod(x, m) {
26 | return (x % m + m) % m;
27 | }
28 |
29 | /**
30 | * Rule 30
31 | * See : https://en.wikipedia.org/wiki/Rule_30
32 | */
33 | function rule30(left, center, right) {
34 | return left ^ (center | right);
35 | }
36 |
37 | /**
38 | * Grid class with cells
39 | */
40 | class Grid {
41 | constructor(gridSize) {
42 | // The row we are currently checking
43 | this.currentRow = 1;
44 |
45 | this.gridSize = gridSize;
46 |
47 | // 2D array of booleans
48 | this.grid = [];
49 |
50 | for (let i = 0; i < gridSize; i++) {
51 | this.grid[i] = [];
52 | }
53 |
54 | // The middle top cell is on
55 | this.grid[gridSize / 2][0] = true;
56 | }
57 |
58 | /**
59 | * Apply a rule to the current row
60 | */
61 | applyRule(rule) {
62 | // Go through every cells in the row
63 | for (let i = 0; i < this.gridSize; i++) {
64 | const pRow = mod(this.currentRow - 1, this.gridSize);
65 |
66 | // Get left, right and center and loop at the corners
67 | const left = this.grid[mod(i - 1, this.gridSize)][pRow];
68 | const center = this.grid[i][pRow];
69 | const right = this.grid[mod(i + 1, this.gridSize)][pRow];
70 |
71 | this.grid[i][this.currentRow] = rule(left, center, right);
72 | }
73 |
74 | this.currentRow = (this.currentRow + 1) % this.gridSize;
75 | }
76 |
77 | display() {
78 | const cellSize = (width - margin * 2) / this.gridSize;
79 |
80 | for (let i = 0; i < this.gridSize; i++) {
81 | let x = margin + i * cellSize;
82 |
83 | for (let j = 0; j < this.gridSize; j++) {
84 | let y = margin + j * cellSize;
85 |
86 | stroke(255);
87 | noFill();
88 | square(x, y, cellSize);
89 |
90 | let chSpace = map(j, 0, this.gridSize * 2, 3, 10);
91 | if (this.grid[i][j]) fillCell(x, y, cellSize, chSpace);
92 | }
93 | }
94 | }
95 |
96 | update() {
97 | this.display();
98 | this.applyRule(rule30);
99 | }
100 | }
101 |
102 | const margin = 50;
103 | const grid = new Grid(30);
104 |
105 | function setup() {
106 | createCanvas(500, 500);
107 | }
108 |
109 | function draw() {
110 | background("#003344");
111 | grid.update();
112 | }
113 |
--------------------------------------------------------------------------------
/2021/day_2/day_2.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | int margin = 50;
5 | int iteration = 0;
6 |
7 | /**
8 | * Fill a cell with diagonal cross hatches
9 | */
10 | void fillCell(float x, float y, float cellSize, float step) {
11 | float offset = step;
12 |
13 | while (offset <= cellSize * 2 - step) {
14 | if (offset < cellSize) {
15 | line(x + offset, y, x, y + offset);
16 | } else {
17 | line(x + cellSize, y + offset - cellSize, x + offset - cellSize, y + cellSize);
18 | }
19 |
20 | offset += step;
21 | }
22 | }
23 |
24 | /**
25 | * Modulo with non negative number (for indices)
26 | * See : https://stackoverflow.com/questions/1082917/mod-of-negative-number-is-melting-my-brain
27 | */
28 | int mod(int x, int m) {
29 | return (x % m + m) % m;
30 | }
31 |
32 | /**
33 | * Rule 30
34 | * See : https://en.wikipedia.org/wiki/Rule_30
35 | */
36 | boolean rule30(boolean left, boolean center, boolean right) {
37 | return left ^ (center | right);
38 | }
39 |
40 | /**
41 | * Grid class with cells
42 | */
43 | class Grid {
44 | boolean[][] grid;
45 | int gridSize;
46 |
47 | // The row we are currently checking
48 | int currentRow;
49 |
50 | Grid(int gridSize) {
51 | this.currentRow = 1;
52 |
53 | this.gridSize = gridSize;
54 | grid = new boolean[gridSize][gridSize];
55 |
56 | // The middle top cell is on
57 | grid[gridSize / 2][0] = true;
58 | }
59 |
60 | /**
61 | * Apply rule 30 to the current row
62 | */
63 | void applyRule30() {
64 | // Go through every cells in the row
65 | for (int i = 0; i < this.gridSize; i++) {
66 | int pRow = mod(this.currentRow - 1, this.gridSize);
67 |
68 | // Get left, right and center and loop at the corners
69 | boolean left = this.grid[mod(i - 1, this.gridSize)][pRow];
70 | boolean center = this.grid[i][pRow];
71 | boolean right = this.grid[mod(i + 1, this.gridSize)][pRow];
72 |
73 | this.grid[i][currentRow] = rule30(left, center, right);
74 | }
75 |
76 | this.currentRow++;
77 | if (currentRow == this.gridSize) {
78 | iteration++;
79 | currentRow = 0;
80 | }
81 | }
82 |
83 | void display() {
84 | float cellSize = (width - margin * 2) / this.gridSize;
85 |
86 | for (int i = 0; i < this.gridSize; i++) {
87 | float x = margin + i * cellSize;
88 |
89 | for (int j = 0; j < this.gridSize; j++) {
90 | float y = margin + j * cellSize;
91 |
92 | stroke(255);
93 | noFill();
94 | square(x, y, cellSize);
95 |
96 | float chSpace = map(j, 0, this.gridSize * 2, 3, 10);
97 | if (this.grid[i][j]) fillCell(x, y, cellSize, chSpace);
98 | }
99 | }
100 | }
101 |
102 | void update() {
103 | this.display();
104 | this.applyRule30();
105 | }
106 | }
107 |
108 | Grid grid;
109 |
110 | void setup() {
111 | size(500, 500);
112 |
113 | grid = new Grid(30);
114 | }
115 |
116 | void draw() {
117 | background(#003344);
118 | grid.update();
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/2021/day_2/genuary_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_2/genuary_2.gif
--------------------------------------------------------------------------------
/2021/day_20/day_20.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Recursive function to imitate a for loop
6 | */
7 | function forLoop(start, condition, update, body) {
8 | if (!condition(start)) return;
9 | body(start);
10 | forLoop(update(start), condition, update, body);
11 | }
12 |
13 | /**
14 | * From : https://easings.net/#easeInOutQuart
15 | */
16 | function easeInOutQuart(x) {
17 | return x < 0.5 ? 8 * x * x * x * x : 1 - pow(-2 * x + 2, 4) / 2;
18 | }
19 |
20 | const nCircles = 5;
21 | let offset = 0;
22 |
23 | function setup() {
24 | createCanvas(500, 500);
25 | }
26 |
27 | function draw() {
28 | background("#003344");
29 |
30 | translate(width / 2, height / 2);
31 |
32 | noFill();
33 | stroke(255);
34 | strokeCap(SQUARE);
35 |
36 | forLoop(
37 | 0,
38 | i => (i <= nCircles),
39 | i => (i + 1),
40 | i => {
41 | const r = i * 60;
42 | const ccos = (easeInOutQuart((cos(offset + i * 0.1) + 1) / 2) - 0.5) * 2;
43 | const ssin = (easeInOutQuart((sin(offset + i * 0.05) + 1) / 2) - 0.5) * 2;
44 |
45 | const startAngle = ccos * TWO_PI + ssin * HALF_PI;
46 | const endAngle = ccos * HALF_PI + ssin * HALF_PI;
47 |
48 | strokeWeight(i + (cos(offset * 2 + i * 0.5) + 1) * 10);
49 |
50 | if (i % 2 == 0) {
51 | stroke("#ff9900");
52 | } else {
53 | stroke(255);
54 | }
55 |
56 | arc(ccos * 80, ssin * 80, r * ccos, r * ccos, min(startAngle, endAngle), max(startAngle, endAngle));
57 | }
58 | );
59 |
60 | offset += 0.03;
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/2021/day_20/day_20.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Generic function interface
6 | */
7 | interface Function {
8 | /**
9 | * Generic call method with variable number of arguments and return type
10 | */
11 | T call(U... args);
12 | }
13 |
14 | /**
15 | * Function that return if the given integer is <= to some value
16 | */
17 | class InferiorOrEqualTo implements Function {
18 | int max;
19 |
20 | InferiorOrEqualTo(int max) {
21 | super();
22 | this.max = max;
23 | }
24 |
25 | Boolean call(Integer... args) {
26 | return args[0] <= max;
27 | }
28 | }
29 |
30 | /**
31 | * Function that return a value incremented by some amount
32 | */
33 | class Increment implements Function {
34 | int incr;
35 |
36 | Increment(int incr) {
37 | this.incr = incr;
38 | }
39 |
40 | Integer call(Integer... args) {
41 | return args[0] + incr;
42 | }
43 | }
44 |
45 | /**
46 | * Body function that draw the arcs, return nothing
47 | */
48 | class Body implements Function {
49 | Void call(Integer... args) {
50 | noFill();
51 | stroke(255);
52 | strokeCap(SQUARE);
53 |
54 | int i = args[0];
55 | float r = i * 60;
56 |
57 | float ccos = (easeInOutQuart((cos(offset + i * 0.1) + 1) / 2.0) - 0.5) * 2;
58 | float ssin = (easeInOutQuart((sin(offset + i * 0.05) + 1) / 2.0) - 0.5) * 2;
59 |
60 | float startAngle = ccos * TWO_PI + ssin * HALF_PI;
61 | float endAngle = ccos * HALF_PI + ssin * HALF_PI;
62 |
63 | strokeWeight(i + (cos(offset * 2 + args[0] * 0.5) + 1) * 10);
64 |
65 | if (i % 2 == 0) {
66 | stroke(#ff9900);
67 | } else {
68 | stroke(255);
69 | }
70 |
71 | arc(ccos * 80, ssin * 80, abs(r * ccos), abs(r * ccos), min(startAngle, endAngle), max(startAngle, endAngle));
72 |
73 | return null;
74 | }
75 | }
76 |
77 | /**
78 | * Recursive function to imitate a for loop
79 | */
80 | void forLoop(int start, Function condition, Function update, Function body) {
81 | if (!condition.call(start)) return;
82 | body.call(start);
83 | forLoop(update.call(start), condition, update, body);
84 | }
85 |
86 | /**
87 | * From : https://easings.net/#easeInOutQuart
88 | */
89 | float easeInOutQuart(float x) {
90 | return x < 0.5 ? 8 * x * x * x * x : 1 - pow(-2 * x + 2, 4) / 2;
91 | }
92 |
93 | final int nCircles = 5;
94 | float offset = 0;
95 |
96 | Function condition, increment, body;
97 |
98 | void setup() {
99 | size(500, 500);
100 |
101 | condition = new InferiorOrEqualTo(nCircles);
102 | increment = new Increment(1);
103 | body = new Body();
104 | }
105 |
106 | void draw() {
107 | background(#003344);
108 |
109 | translate(width / 2, height / 2);
110 |
111 | forLoop(0, condition, increment, body);
112 |
113 | offset += 0.03;
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/2021/day_20/genuary_20.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_20/genuary_20.gif
--------------------------------------------------------------------------------
/2021/day_21/day_21.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // Min and max values of x
5 | const MIN_VALUE = 10;
6 | const MAX_VALUE = 200;
7 |
8 | /**
9 | * Main draw function, the offset controls the animation
10 | */
11 | function DRAW(x, offset) {
12 | // Normalized value from 0 to 1 for a call with x
13 | const norma = map(x, MIN_VALUE, MAX_VALUE, 0, 1);
14 |
15 | push();
16 |
17 | rotate(norma * TWO_PI + offset + cos(x / 100 + offset) * HALF_PI);
18 |
19 | strokeWeight(x / 20);
20 | noFill();
21 | stroke(255, 80);
22 | ellipse(0, 0, x, x * 2);
23 |
24 | stroke(255, 153, 0, (1 - norma) * 255);
25 | strokeWeight(norma * 50);
26 | point(x, norma * x + cos(offset) * 50);
27 |
28 | rotate(PI);
29 | point(x, norma * x + sin(offset) * 50);
30 |
31 | pop();
32 | }
33 |
34 | /**
35 | * From : https://genuary2021.github.io/prompts#jan15
36 | */
37 | function f(x, offset) {
38 | if (x < MIN_VALUE) return;
39 | DRAW(x, offset);
40 | f(1 * x / 4, offset);
41 | f(2 * x / 4, offset);
42 | f(3 * x / 4, offset);
43 | }
44 |
45 | let offset = 0;
46 |
47 | function setup() {
48 | createCanvas(500, 500);
49 | }
50 |
51 | function draw() {
52 | background("#003344");
53 |
54 | translate(width / 2, height / 2);
55 | f(MAX_VALUE, offset);
56 |
57 | offset += 0.02;
58 | }
59 |
--------------------------------------------------------------------------------
/2021/day_21/day_21.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // Min and max values of x
5 | final float MIN_VALUE = 10;
6 | final float MAX_VALUE = 200;
7 |
8 | /**
9 | * Main draw function, the offset controls the animation
10 | */
11 | void DRAW(float x, float offset) {
12 | // Normalized value from 0 to 1 for a call with x
13 | float norma = map(x, MIN_VALUE, MAX_VALUE, 0, 1);
14 |
15 | push();
16 |
17 | rotate(norma * TWO_PI + offset + cos(x / 100 + offset) * HALF_PI);
18 |
19 | strokeWeight(x / 20);
20 | noFill();
21 | stroke(255, 80);
22 | ellipse(0, 0, x, x * 2);
23 |
24 | stroke(255, 153, 0, (1 - norma) * 255);
25 | strokeWeight(norma * 50);
26 | point(x, norma * x + cos(offset) * 50);
27 |
28 | rotate(PI);
29 | point(x, norma * x + sin(offset) * 50);
30 |
31 | pop();
32 | }
33 |
34 | /**
35 | * From : https://genuary2021.github.io/prompts#jan15
36 | */
37 | void f(float x, float offset) {
38 | if (x < MIN_VALUE) return;
39 | DRAW(x, offset);
40 | f(1 * x / 4, offset);
41 | f(2 * x / 4, offset);
42 | f(3 * x / 4, offset);
43 | }
44 |
45 | float offset = 0;
46 |
47 | void setup() {
48 | size(500, 500);
49 | }
50 |
51 | void draw() {
52 | background(#003344);
53 |
54 | translate(width / 2, height / 2);
55 | f(MAX_VALUE, offset);
56 |
57 | offset += 0.02;
58 | }
59 |
--------------------------------------------------------------------------------
/2021/day_21/genuary_21.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_21/genuary_21.gif
--------------------------------------------------------------------------------
/2021/day_22/day_22.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Draw quads with diferent thickness and sizes to display lines
6 | */
7 | function pseudoLine(divisions, thickness, margin, offset) {
8 | const points = [];
9 |
10 | const newWidth = width - 2 * margin;
11 | const newHeight = height - 2 * margin;
12 |
13 | // Compute the middle points of the quads for faster lookup
14 | for (let i = 0; i <= divisions; i++) {
15 | const n = map(i, 0, divisions, 0, 1);
16 |
17 | const nFromCenter = abs(0.5 - n) * 2;
18 |
19 | //const offsetX = sin(n * TWO_PI * 2 + offset) * 100;
20 | const offsetX = n * newWidth;
21 | const offsetY = sin(n * TWO_PI + offset) * 40 * (1 - nFromCenter);
22 |
23 | const offRad = (1 - nFromCenter) * 50;
24 |
25 | points.push({
26 | x: offsetX + cos(n * TWO_PI + offset) * offRad,
27 | y: margin + i * (newHeight / divisions) + offsetY + sin(n * TWO_PI * 10 + offset) * offRad,
28 | normFromCenter: nFromCenter
29 | });
30 | }
31 |
32 | for (let i = 0; i < divisions; i++) {
33 | const current = points[i];
34 | const next = points[i + 1];
35 |
36 | fill(255, 153, 0);
37 |
38 | // Stroke same color as background to "erase" stuff
39 | stroke("#003344");
40 |
41 | strokeWeight(thickness * 1 / (current.normFromCenter + 2.5));
42 |
43 | quad(margin, current.y,
44 | margin + current.x, current.y,
45 | margin + next.x, next.y,
46 | margin, next.y);
47 |
48 | quad(margin + current.x, current.y,
49 | width - margin, current.y,
50 | width - margin, next.y,
51 | margin + next.x, next.y);
52 | }
53 | }
54 |
55 | let offset = 0;
56 | const margin = 80;
57 |
58 | function setup() {
59 | createCanvas(500, 500);
60 | }
61 |
62 | function draw() {
63 | background("#003344");
64 |
65 | pseudoLine(20, 10, margin, offset);
66 |
67 | offset += 0.02;
68 | }
69 |
--------------------------------------------------------------------------------
/2021/day_22/day_22.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Custom class to wrap data like on the fly objects with js
6 | */
7 | class CustomPos {
8 | float x, y;
9 | float normFromCenter;
10 |
11 | CustomPos(float x, float y, float normFromCenter) {
12 | this.x = x;
13 | this.y = y;
14 | this.normFromCenter = normFromCenter;
15 | }
16 | }
17 |
18 | /**
19 | * Draw quads with diferent thickness and sizes to display lines
20 | */
21 | void pseudoLine(int divisions, float thickness, float margin, float offset) {
22 | CustomPos[] points = new CustomPos[divisions + 1];
23 |
24 | float newWidth = width - 2 * margin;
25 | float newHeight = height - 2 * margin;
26 |
27 | // Compute the middle points of the quads for faster lookup
28 | for (int i = 0; i <= divisions; i++) {
29 | float n = map(i, 0, divisions, 0, 1);
30 |
31 | float nFromCenter = abs(0.5 - n) * 2;
32 |
33 | //const offsetX = sin(n * TWO_PI * 2 + offset) * 100;
34 | float offsetX = n * newWidth;
35 | float offsetY = sin(n * TWO_PI + offset) * 40 * (1 - nFromCenter);
36 |
37 | float offRad = (1 - nFromCenter) * 50;
38 |
39 | points[i] = new CustomPos(
40 | offsetX + cos(n * TWO_PI + offset) * offRad,
41 | margin + i * (newHeight / divisions) + offsetY + sin(n * TWO_PI * 10 + offset) * offRad,
42 | nFromCenter
43 | );
44 | }
45 |
46 | for (int i = 0; i < divisions; i++) {
47 | CustomPos current = points[i];
48 | CustomPos next = points[i + 1];
49 |
50 | fill(255, 153, 0);
51 |
52 | // Stroke same color as background to "erase" stuff
53 | stroke(#003344);
54 |
55 | strokeWeight(thickness * 1 / (current.normFromCenter + 2.5));
56 |
57 | quad(margin, current.y,
58 | margin + current.x, current.y,
59 | margin + next.x, next.y,
60 | margin, next.y);
61 |
62 | quad(margin + current.x, current.y,
63 | width - margin, current.y,
64 | width - margin, next.y,
65 | margin + next.x, next.y);
66 | }
67 | }
68 |
69 | float offset = 0;
70 | final int margin = 80;
71 |
72 | void setup() {
73 | size(500, 500);
74 | }
75 |
76 | void draw() {
77 | background(#003344);
78 |
79 | pseudoLine(20, 10, margin, offset);
80 |
81 | offset += 0.02;
82 | }
83 |
--------------------------------------------------------------------------------
/2021/day_22/genuary_22.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_22/genuary_22.gif
--------------------------------------------------------------------------------
/2021/day_23/day_23.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | let colors;
5 | let offset = 0;
6 |
7 | /**
8 | * Display nCircles circles at location (x, y) with specified
9 | * max and min diameter. Use the offset to control the animation
10 | */
11 | function circles(x, y, nCircles, maxDiam, minDiam, offset) {
12 | push();
13 | translate(x, y);
14 |
15 | for (let i = 0; i < nCircles; i++) {
16 | const n = map(i, 0, nCircles - 1, 0, 1);
17 |
18 | const diameter = map(n, 0, 1, maxDiam, minDiam);
19 |
20 | const x = i * n * cos(offset) * 10;
21 | const y = (diameter / 2) * n * sin(offset);
22 |
23 | noStroke();
24 |
25 | // Shadow
26 | fill(0, 30);
27 | circle(x + 5, y + 2, diameter);
28 |
29 | // Colored circle
30 | const colorIndex = i % colors.length;
31 | fill(colors[colorIndex]);
32 | circle(x, y, diameter);
33 | }
34 |
35 | pop();
36 | }
37 |
38 | function setup() {
39 | createCanvas(500, 500);
40 |
41 | // Shuffle the colors randomly at each run
42 | // From : https://flaviocopes.com/how-to-shuffle-array-javascript/
43 | colors = ["#264653", "#2a9d8f", "#e9c46a", "#f4a261", "#e76f51"].map(c => color(c)).sort(() => Math.random() - 0.5);
44 | }
45 |
46 | function draw() {
47 | background("#003344");
48 |
49 | circles(width / 2, height / 2, 10, 300, 50, cos(offset) - sin(offset) * log((cos(offset) + 1) * TWO_PI));
50 |
51 | offset += 0.05;
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/2021/day_23/day_23.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | color[] colors = {#264653, #2a9d8f, #e9c46a, #f4a261, #e76f51};
5 | float offset = 0;
6 |
7 | void shuffleArray(color[] colors) {
8 | for (int i = colors.length - 1; i >= 1; i--) {
9 | int j = int(random(i));
10 | int temp = colors[j];
11 | colors[j] = colors[i];
12 | colors[i] = temp;
13 | }
14 | }
15 |
16 | /**
17 | * Display nCircles circles at location (x, y) with specified
18 | * max and min diameter. Use the offset to control the animation
19 | */
20 | void circles(float x, float y, int nCircles, float maxDiam, float minDiam, float offset) {
21 | push();
22 | translate(x, y);
23 |
24 | for (int i = 0; i < nCircles; i++) {
25 | float n = map(i, 0, nCircles - 1, 0, 1);
26 |
27 | float diameter = map(n, 0, 1, maxDiam, minDiam);
28 |
29 | float xx = i * n * cos(offset) * 10;
30 | float yy = (diameter / 2) * n * sin(offset);
31 |
32 | noStroke();
33 |
34 | // Shadow
35 | fill(0, 30);
36 | circle(xx + 5, yy + 2, diameter);
37 |
38 | // Colored circle
39 | int colorIndex = i % colors.length;
40 | fill(colors[colorIndex]);
41 | circle(xx, yy, diameter);
42 | }
43 |
44 | pop();
45 | }
46 |
47 | void setup() {
48 | size(500, 500);
49 |
50 | // Shuffle the colors randomly at each run
51 | // From : https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
52 | shuffleArray(colors);
53 | }
54 |
55 | void draw() {
56 | background(#003344);
57 |
58 | circles(width / 2, height / 2, 10, 300, 50, cos(offset) - sin(offset) * log((cos(offset) + 1) * TWO_PI));
59 |
60 | offset += 0.03;
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/2021/day_23/genuary_23.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_23/genuary_23.gif
--------------------------------------------------------------------------------
/2021/day_24/day_24.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Display a square of n * n lines
6 | */
7 | function squareOfLines(x, y, n, size, offset) {
8 | push();
9 | translate(x, y);
10 |
11 | const cellSize = size / n;
12 |
13 | for (let i = 0; i < n; i++) {
14 | const xx = i * cellSize;
15 | for (let j = 0; j < n; j++) {
16 | const yy = j * cellSize;
17 |
18 | const noiseOffset = noise(i + cos(offset) * 5, j + sin(offset) * 5);
19 |
20 | stroke("#ff9900");
21 | strokeWeight(noiseOffset * 4);
22 | line(xx, yy + cellSize * (1 - noiseOffset), xx + cellSize * noiseOffset * cos(offset + i / 10) * 2, yy );
23 | }
24 | }
25 |
26 | pop();
27 | }
28 |
29 | let offset = 0;
30 |
31 | // Margin between the grid and the canvas
32 | const margin = 50;
33 |
34 | // Gap between squares of lines
35 | const gap = 5;
36 |
37 | // Size of the square of lines
38 | const n = 5;
39 |
40 | let totalSize, cellSize, start;
41 |
42 |
43 | function setup() {
44 | createCanvas(500, 500);
45 |
46 | totalSize = (width - 2 * margin);
47 | cellSize = (totalSize - gap * (n - 1)) / n;
48 | start = (width - totalSize) / 2;
49 | }
50 |
51 | function draw() {
52 | background("#003344");
53 |
54 | for (let i = 0; i < n; i++) {
55 | const x = start + i * (cellSize + gap);
56 | for (let j = 0; j < n; j++) {
57 | const y = start + j * (cellSize + gap);
58 |
59 | squareOfLines(x, y, 10, cellSize, offset + i + j);
60 | }
61 | }
62 |
63 | offset += 0.05;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/2021/day_24/day_24.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Display a square of n * n lines
6 | */
7 | void squareOfLines(float x, float y, float n, float size, float offset) {
8 | push();
9 | translate(x, y);
10 |
11 | float cellSize = size / n;
12 |
13 | for (int i = 0; i < n; i++) {
14 | float xx = i * cellSize;
15 | for (int j = 0; j < n; j++) {
16 | float yy = j * cellSize;
17 |
18 | float noiseOffset = noise(i + cos(offset) * 5, j + sin(offset) * 5);
19 |
20 | stroke(#ff9900);
21 | strokeWeight(noiseOffset * 4);
22 | line(xx, yy + cellSize * (1 - noiseOffset), xx + cellSize * noiseOffset * cos(offset + i / 10) * 2, yy );
23 | }
24 | }
25 |
26 | pop();
27 | }
28 |
29 | float offset = 0;
30 |
31 | // Margin between the grid and the canvas
32 | float margin = 50;
33 |
34 | // Gap between squares of lines
35 | float gap = 5;
36 |
37 | // Size of the square of lines
38 | int n = 5;
39 |
40 | float totalSize, cellSize, start;
41 |
42 |
43 | void setup() {
44 | size(500, 500);
45 |
46 | totalSize = (width - 2 * margin);
47 | cellSize = (totalSize - gap * (n - 1)) / n;
48 | start = (width - totalSize) / 2;
49 | }
50 |
51 | void draw() {
52 | background(#003344);
53 |
54 | for (int i = 0; i < n; i++) {
55 | float x = start + i * (cellSize + gap);
56 | for (int j = 0; j < n; j++) {
57 | float y = start + j * (cellSize + gap);
58 |
59 | squareOfLines(x, y, 10, cellSize, offset + i + j);
60 | }
61 | }
62 |
63 | offset += 0.03;
64 | }
65 |
--------------------------------------------------------------------------------
/2021/day_24/genuary_24.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_24/genuary_24.gif
--------------------------------------------------------------------------------
/2021/day_25/day_25.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | function easeInOutCubic(x) {
5 | return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
6 | }
7 |
8 | class Permutation {
9 | constructor(i, j, startTime, duration) {
10 | this.indices = [i, j];
11 | this.startTime = startTime;
12 | this.duration = duration;
13 | }
14 |
15 | /**
16 | * Return the current duration of the permutation since the starting time
17 | */
18 | getCurrentDuration() {
19 | return window.performance.now() - this.startTime;
20 | }
21 |
22 | /**
23 | * Return true if the permutation is over
24 | */
25 | isOver() {
26 | return this.getCurrentDuration() >= this.duration;
27 | }
28 |
29 | /**
30 | * Return a normalized duration value between [0, 1]
31 | */
32 | getNormalizedTime() {
33 | return this.getCurrentDuration() / this.duration;
34 | }
35 |
36 | /**
37 | * Return true if i is the first index of the permutation
38 | */
39 | isFirst(i) {
40 | return i == this.indices[0];
41 | }
42 |
43 | /**
44 | * Return the distance between the two indices
45 | * Can be negative
46 | */
47 | getDistance() {
48 | return this.indices[1] - this.indices[0];
49 | }
50 | }
51 |
52 |
53 | class Set {
54 | constructor(size) {
55 | this.elements = new Array(size);
56 |
57 | for (let i = 0; i < size; i++) {
58 | this.elements
59 | }
60 |
61 | // Store a list of permutations
62 | this.permutations = [];
63 | }
64 |
65 | /**
66 | * Return the permutation of the corresponding element at index i
67 | * Return null if not found
68 | */
69 | getPermutation(i) {
70 | for (let j = this.permutations.length - 1; j >= 0; j--) {
71 | const perm = this.permutations[j];
72 | if (perm.indices.includes(i)) {
73 | return perm;
74 | }
75 | }
76 |
77 | return null;
78 | }
79 |
80 | /**
81 | * Permute two elements at location i and j
82 | * Return true if succeded
83 | */
84 | permute(i, j, duration) {
85 | // Check for collision
86 | if (this.getPermutation(i) || this.getPermutation(j)) {
87 | return false;
88 | }
89 |
90 | // Otherwise add a new permutation
91 | this.permutations.push(new Permutation(i, j, window.performance.now(), duration));
92 |
93 | return true;
94 | }
95 |
96 | randomPermute(duration) {
97 | const i = int(random(this.elements.length));
98 | const j = int(random(this.elements.length));
99 | this.permute(i, j, duration);
100 | }
101 |
102 | update() {
103 | for (let i = this.permutations.length - 1; i >= 0; i--) {
104 | const perm = this.permutations[i];
105 |
106 | // If the permutation is over, delete it
107 | if (perm.isOver()) {
108 | this.permutations.pop();
109 | }
110 | }
111 | }
112 |
113 | display(centerX, centerY, length) {
114 | const squareSize = length / this.elements.length;
115 |
116 | push();
117 | translate(centerX - length / 2 + squareSize / 2, centerY);
118 |
119 | fill("#ff9900");
120 | stroke("#003344");
121 |
122 | strokeWeight(10);
123 |
124 | rectMode(CENTER);
125 |
126 | for (let i = 0; i < this.elements.length; i++) {
127 | const x = i * squareSize;
128 | const perm = this.getPermutation(i);
129 |
130 | if (perm) {
131 | const n = easeInOutCubic(perm.getNormalizedTime());
132 | const distance = perm.getDistance();
133 | const factor = perm.isFirst(i) ? 1 : -1;
134 |
135 | const radius = (distance / 2) * squareSize;
136 |
137 | const circleCenterX = x + radius * factor;
138 |
139 | const angle = n * PI + (perm.isFirst(i) ? PI : 0);
140 | const circleX = Math.cos(angle) * Math.abs(radius);
141 | const circleY = Math.sin(angle) * Math.abs(radius);
142 |
143 | push();
144 |
145 | translate(circleCenterX + circleX, circleY);
146 | rotate(n * TWO_PI * factor);
147 | square(0, 0, squareSize);
148 |
149 | pop();
150 | } else {
151 | square(x, 0, squareSize);
152 | }
153 | }
154 |
155 | pop();
156 | }
157 | }
158 |
159 | const sets = [];
160 | const nSets = 10;
161 | const size = 300;
162 |
163 | let time = 0;
164 |
165 | function setup() {
166 | createCanvas(500, 500);
167 |
168 | for (let i = 0; i < nSets; i++) {
169 | sets.push(new Set(nSets));
170 | }
171 | }
172 |
173 | function draw() {
174 | background("#003344");
175 |
176 | const rowHeight = size / nSets;
177 | const startY = height / 2 - size / 2 + rowHeight / 2;
178 | for (let i = 0; i < nSets; i++) {
179 | sets[i].display(width / 2, startY + i * rowHeight, 300);
180 | sets[i].update();
181 | }
182 |
183 | // Every 2s, add some random permutations
184 | if (millis() - time > 2000) {
185 | for (let i = 0; i < 10; i++) sets[int(random(sets.length))].randomPermute(random(1000, 2000));
186 |
187 | time = millis();
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/2021/day_25/day_25.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | float easeInOutCubic(float x) {
7 | return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
8 | }
9 |
10 | class Permutation {
11 | int[] indices;
12 | float duration, maxDuration;
13 |
14 | Permutation(int i, int j, float duration) {
15 | this.indices = new int[2];
16 | this.indices[0] = i;
17 | this.indices[1] = j;
18 | this.maxDuration = duration;
19 | this.duration = duration;
20 | }
21 |
22 | /**
23 | * Return the current duration of the permutation since the starting time
24 | */
25 | float getCurrentDuration() {
26 | return this.duration;
27 | }
28 |
29 | /**
30 | * Return true if the permutation is over
31 | */
32 | boolean isOver() {
33 | return this.duration <= 0;
34 | }
35 |
36 | /**
37 | * Return a normalized duration value between [0, 1]
38 | */
39 | float getNormalizedTime() {
40 | return this.getCurrentDuration() / (float) this.maxDuration;
41 | }
42 |
43 | /**
44 | * Return true if i is the first index of the permutation
45 | */
46 | boolean isFirst(int i) {
47 | return i == this.indices[0];
48 | }
49 |
50 | /**
51 | * Return the distance between the two indices
52 | * Can be negative
53 | */
54 | int getDistance() {
55 | return this.indices[1] - this.indices[0];
56 | }
57 | }
58 |
59 |
60 | class Set {
61 | float[] elements;
62 | List permutations;
63 |
64 | Set(int size) {
65 | this.elements = new float[size];
66 |
67 | for (int i = 0; i < size; i++) {
68 | this.elements[i] = random(1);
69 | }
70 |
71 | // Store a list of permutations
72 | this.permutations = new ArrayList();
73 | }
74 |
75 | /**
76 | * Return the permutation of the corresponding element at index i
77 | * Return null if not found
78 | */
79 | Permutation getPermutation(int i) {
80 | for (int j = this.permutations.size() - 1; j >= 0; j--) {
81 | Permutation perm = this.permutations.get(j);
82 | if (perm.indices[0] == i || perm.indices[1] == i) {
83 | return perm;
84 | }
85 | }
86 |
87 | return null;
88 | }
89 |
90 | /**
91 | * Permute two elements at location i and j
92 | * Return true if succeded
93 | */
94 | boolean permute(int i, int j, int duration) {
95 | // Check for collision
96 | if (this.getPermutation(i) != null || this.getPermutation(j) != null) {
97 | return false;
98 | }
99 |
100 | // Otherwise add a new permutation
101 | this.permutations.add(new Permutation(i, j, duration));
102 |
103 | return true;
104 | }
105 |
106 | void randomPermute(int duration) {
107 | int i = int(random(this.elements.length));
108 | int j = int(random(this.elements.length));
109 | this.permute(i, j, duration);
110 | }
111 |
112 | void update() {
113 | for (int i = this.permutations.size() - 1; i >= 0; i--) {
114 | Permutation perm = this.permutations.get(i);
115 |
116 | perm.duration -= 30;
117 |
118 | // If the permutation is over, delete it
119 | if (perm.isOver()) {
120 | this.permutations.remove(this.permutations.size() - 1);
121 | }
122 | }
123 | }
124 |
125 | void display(float centerX, float centerY, float length) {
126 | float squareSize = length / this.elements.length;
127 |
128 | push();
129 | translate(centerX - length / 2 + squareSize / 2, centerY);
130 |
131 | stroke(#003344);
132 |
133 | strokeWeight(10);
134 |
135 | rectMode(CENTER);
136 |
137 | for (int i = 0; i < this.elements.length; i++) {
138 | float x = i * squareSize;
139 | Permutation perm = this.getPermutation(i);
140 |
141 | fill(lerpColor(color(#ff9900), color(#ff4d00), this.elements[i]));
142 |
143 | if (perm != null) {
144 | float n = easeInOutCubic(perm.getNormalizedTime());
145 | float distance = perm.getDistance();
146 | int factor = perm.isFirst(i) ? 1 : -1;
147 |
148 | float radius = (distance / 2) * squareSize;
149 |
150 | float circleCenterX = x + radius * factor;
151 |
152 | float angle = n * PI + (perm.isFirst(i) ? PI : 0);
153 | float circleX = cos(angle) * abs(radius);
154 | float circleY = sin(angle) * abs(radius);
155 |
156 | push();
157 |
158 | translate(circleCenterX + circleX, circleY);
159 | rotate(n * TWO_PI * factor);
160 | square(0, 0, squareSize);
161 |
162 | pop();
163 | } else {
164 |
165 | square(x, 0, squareSize);
166 | }
167 | }
168 |
169 | pop();
170 | }
171 | }
172 |
173 | List sets = new ArrayList();
174 | int nSets = 10;
175 | int size = 300;
176 |
177 | int time = 0;
178 |
179 | void setup() {
180 | size(500, 500);
181 |
182 | for (int i = 0; i < nSets; i++) {
183 | sets.add(new Set(nSets));
184 | }
185 |
186 | for (int i = 0; i < 20; i++) sets.get(int(random(sets.size()))).randomPermute(int(random(2000, 3000)));
187 | }
188 |
189 | void draw() {
190 | background(#003344);
191 |
192 | float rowHeight = size / nSets;
193 | float startY = height / 2 - size / 2 + rowHeight / 2;
194 |
195 | for (int i = 0; i < nSets; i++) {
196 | sets.get(i).display(width / 2, startY + i * rowHeight, 300);
197 | sets.get(i).update();
198 | }
199 |
200 | // Every 2s, add some random permutations
201 | if (millis() - time > 2000) {
202 | for (int i = 0; i < 20; i++) sets.get(int(random(sets.size()))).randomPermute(int(random(2000, 3000)));
203 | time = millis();
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/2021/day_25/genuary_25.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_25/genuary_25.gif
--------------------------------------------------------------------------------
/2021/day_26/day_26.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Line class that contains a linear equation y = a * x + b
6 | * can't be vertical by definition
7 | */
8 | class Line {
9 | constructor(p1, p2) {
10 | this.start = p1;
11 | this.end = p2;
12 |
13 | this.a = (p2.y - p1.y) / (p2.x - p1.x);
14 | this.b = p1.y - p1.x * this.a;
15 | }
16 |
17 | /**
18 | * Return y coordinate of point at location x
19 | */
20 | getYAt(x) {
21 | return this.a * x + this.b;
22 | }
23 |
24 | /**
25 | * Compute the intersection of two lines
26 | * Return null if they are parallel
27 | */
28 | intersect(line) {
29 | const sub = this.a - line.a;
30 | if (sub === 0) return null;
31 |
32 | const x = (line.b - this.b) / sub;
33 | const y = this.getYAt(x);
34 | return createVector(x, y);
35 | }
36 |
37 | /**
38 | * Display the line between original end and last points
39 | */
40 | display() {
41 | line(this.start.x, this.start.y, this.end.x, this.end.y);
42 | }
43 | }
44 |
45 | /**
46 | * Vertical line class, that is x = j
47 | */
48 | class VerticalLine {
49 | constructor(x) {
50 | this.x = x;
51 | }
52 |
53 | /**
54 | * Compute the intersection with a line
55 | * Return null otherwise
56 | */
57 | intersect(line) {
58 | if (!(line instanceof Line)) return null;
59 | return createVector(this.x, line.getYAt(this.x));
60 | }
61 |
62 | /**
63 | * Display the vertical line between yMin and yMax
64 | */
65 | display(yMin, yMax) {
66 | line(this.x, yMin, this.x, yMax);
67 | }
68 | }
69 |
70 | /**
71 | * Return the geometric middle point between two points
72 | */
73 | function getMiddlePoint(p1, p2) {
74 | return createVector((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
75 | }
76 |
77 | /**
78 | * Draw a point from a vector object
79 | */
80 | function drawVertex(p) {
81 | point(p.x, p.y);
82 | }
83 |
84 | /**
85 | * Draw a quad given 4 points as vectors
86 | */
87 | function drawQuad(a, b, c, d) {
88 | quad(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y);
89 | }
90 |
91 | class PerspectiveCube {
92 | constructor(x1, y1, x2, y2, cx, cy, size) {
93 | this.vp1 = createVector(x1, y1);
94 | this.vp2 = createVector(x2, y2);
95 | this.centerCorner = createVector(cx, cy);
96 | this.size = size;
97 | }
98 |
99 | display() {
100 | const downCorner = p5.Vector.add(this.centerCorner, createVector(0, this.size));
101 |
102 | // Up
103 | const vline1Up = new Line(this.vp1, this.centerCorner);
104 | const vline2Up = new Line(this.vp2, this.centerCorner);
105 |
106 | // Down
107 | const vline1Down = new Line(this.vp1, downCorner);
108 | const vline2Down = new Line(this.vp2, downCorner);
109 |
110 | // Frontal edge
111 | const frontEdge = new VerticalLine(this.centerCorner.x);
112 |
113 | const leftUpCorner = getMiddlePoint(this.vp1, this.centerCorner);
114 | const leftEdge = new VerticalLine(leftUpCorner.x);
115 |
116 | const rightUpCorner = getMiddlePoint(this.vp2, this.centerCorner);
117 | const rightEdge = new VerticalLine(rightUpCorner.x);
118 |
119 | const leftDownCorner = getMiddlePoint(this.vp1, downCorner);
120 | const rightDownCorner = getMiddlePoint(this.vp2, downCorner);
121 |
122 | const vline1BackUp = new Line(this.vp1, rightUpCorner);
123 | const vline2BackUp = new Line(this.vp2, leftUpCorner);
124 |
125 | const backCornerUp = vline1BackUp.intersect(vline2BackUp);
126 |
127 | const vline1BackDown = new Line(this.vp1, rightDownCorner);
128 | const vline2BackDown = new Line(this.vp2, leftDownCorner);
129 |
130 | const backCornerDown = vline1BackDown.intersect(vline2BackDown);
131 | const backEdge = new VerticalLine(backCornerUp.x);
132 |
133 | stroke(255, 153, 0);
134 |
135 | strokeWeight(1);
136 | leftEdge.display(leftUpCorner.y, leftDownCorner.y);
137 | rightEdge.display(rightUpCorner.y, rightDownCorner.y);
138 |
139 | vline1BackUp.display();
140 | vline2BackUp.display();
141 |
142 | vline1BackDown.display();
143 | vline2BackDown.display();
144 |
145 | vline1Up.display();
146 | vline2Up.display();
147 |
148 | vline1Down.display();
149 | vline2Down.display();
150 |
151 | frontEdge.display(this.centerCorner.y, downCorner.y);
152 |
153 | strokeWeight(10);
154 | drawVertex(leftUpCorner);
155 | drawVertex(rightUpCorner);
156 | drawVertex(leftDownCorner);
157 | drawVertex(rightDownCorner);
158 | drawVertex(this.vp1);
159 | drawVertex(this.vp2);
160 |
161 | drawVertex(this.centerCorner);
162 | drawVertex(downCorner);
163 |
164 | drawVertex(backCornerUp);
165 |
166 | // Display back semi transparent
167 | stroke(255, 153, 0, 100);
168 |
169 | strokeWeight(10);
170 | drawVertex(backCornerDown);
171 |
172 | strokeWeight(2);
173 | backEdge.display(backCornerUp.y, backCornerDown.y);
174 |
175 | fill(255, 153, 0, 100);
176 | drawQuad(leftUpCorner, backCornerUp, backCornerDown, leftDownCorner);
177 | drawQuad(rightUpCorner, backCornerUp, backCornerDown, rightDownCorner);
178 |
179 | drawQuad(rightUpCorner, this.centerCorner, downCorner, rightDownCorner);
180 | drawQuad(leftUpCorner, this.centerCorner, downCorner, leftDownCorner);
181 |
182 | fill(255, 153, 50, 150);
183 | drawQuad(this.centerCorner, leftUpCorner, backCornerUp, rightUpCorner);
184 | drawQuad(downCorner, leftDownCorner, backCornerDown, rightDownCorner);
185 | }
186 | }
187 |
188 | let cube;
189 | let offset = 0;
190 |
191 | function setup() {
192 | createCanvas(500, 500);
193 |
194 | cube = new PerspectiveCube(50, 150, width - 50, 150, width / 2, height / 2, 150);
195 | }
196 |
197 | function draw() {
198 | background("#003344");
199 |
200 | cube.centerCorner.x = width / 2 + sin(offset / 2) * cos(offset) * 100;
201 | cube.centerCorner.y = height / 2 + sin(offset / 2) * 50;
202 |
203 | cube.vp1.x = 100 + sin(offset / 2) * 50;
204 | cube.vp2.x = height - 100 + cos(offset / 2) * 50;
205 |
206 | cube.vp1.y = height / 2 + sin(-offset) * 100;
207 | cube.vp2.y = height / 2 + cos(-offset) * 100;
208 |
209 | cube.display();
210 |
211 | offset += 0.03;
212 | }
213 |
214 |
--------------------------------------------------------------------------------
/2021/day_26/day_26.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Line class that contains a linear equation y = a * x + b
6 | * can't be vertical by definition
7 | */
8 | class Line {
9 | PVector start, end, p1, p2;
10 | float a, b;
11 |
12 | Line(PVector p1, PVector p2) {
13 | this.start = p1;
14 | this.end = p2;
15 |
16 | this.a = (p2.y - p1.y) / (p2.x - p1.x);
17 | this.b = p1.y - p1.x * this.a;
18 | }
19 |
20 | /**
21 | * Return y coordinate of point at location x
22 | */
23 | float getYAt(float x) {
24 | return this.a * x + this.b;
25 | }
26 |
27 | /**
28 | * Compute the intersection of two lines
29 | * Return null if they are parallel
30 | */
31 | PVector intersect(Line line) {
32 | float sub = this.a - line.a;
33 | if (sub == 0) return null;
34 |
35 | float x = (line.b - this.b) / sub;
36 | float y = this.getYAt(x);
37 | return new PVector(x, y);
38 | }
39 |
40 | /**
41 | * Display the line between original end and last points
42 | */
43 | void display() {
44 | line(this.start.x, this.start.y, this.end.x, this.end.y);
45 | }
46 | }
47 |
48 | /**
49 | * Vertical line class, that is x = j
50 | */
51 | class VerticalLine {
52 | float x;
53 |
54 | VerticalLine(float x) {
55 | this.x = x;
56 | }
57 |
58 | /**
59 | * Compute the intersection with a line
60 | * Return null otherwise
61 | */
62 | PVector intersect(Line line) {
63 | return new PVector(this.x, line.getYAt(this.x));
64 | }
65 |
66 | /**
67 | * Display the vertical line between yMin and yMax
68 | */
69 | void display(float yMin, float yMax) {
70 | line(this.x, yMin, this.x, yMax);
71 | }
72 | }
73 |
74 | /**
75 | * Return the geometric middle point between two points
76 | */
77 | PVector getMiddlePoint(PVector p1, PVector p2) {
78 | return new PVector((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
79 | }
80 |
81 | /**
82 | * Draw a point from a vector object
83 | */
84 | void drawVertex(PVector p) {
85 | point(p.x, p.y);
86 | }
87 |
88 | /**
89 | * Draw a quad given 4 points as vectors
90 | */
91 | void drawQuad(PVector a, PVector b, PVector c, PVector d) {
92 | quad(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y);
93 | }
94 |
95 | class PerspectiveCube {
96 | PVector vp1, vp2, centerCorner;
97 | float size;
98 |
99 | PerspectiveCube(float x1, float y1, float x2, float y2, float cx, float cy, float size) {
100 | this.vp1 = new PVector(x1, y1);
101 | this.vp2 = new PVector(x2, y2);
102 | this.centerCorner = new PVector(cx, cy);
103 | this.size = size;
104 | }
105 |
106 | void display() {
107 | PVector downCorner = PVector.add(this.centerCorner, new PVector(0, this.size));
108 |
109 | // Up
110 | Line vline1Up = new Line(this.vp1, this.centerCorner);
111 | Line vline2Up = new Line(this.vp2, this.centerCorner);
112 |
113 | // Down
114 | Line vline1Down = new Line(this.vp1, downCorner);
115 | Line vline2Down = new Line(this.vp2, downCorner);
116 |
117 | // Frontal edge
118 | VerticalLine frontEdge = new VerticalLine(this.centerCorner.x);
119 |
120 | PVector leftUpCorner = getMiddlePoint(this.vp1, this.centerCorner);
121 | VerticalLine leftEdge = new VerticalLine(leftUpCorner.x);
122 |
123 | PVector rightUpCorner = getMiddlePoint(this.vp2, this.centerCorner);
124 | VerticalLine rightEdge = new VerticalLine(rightUpCorner.x);
125 |
126 | PVector leftDownCorner = getMiddlePoint(this.vp1, downCorner);
127 | PVector rightDownCorner = getMiddlePoint(this.vp2, downCorner);
128 |
129 | Line vline1BackUp = new Line(this.vp1, rightUpCorner);
130 | Line vline2BackUp = new Line(this.vp2, leftUpCorner);
131 |
132 | PVector backCornerUp = vline1BackUp.intersect(vline2BackUp);
133 |
134 | Line vline1BackDown = new Line(this.vp1, rightDownCorner);
135 | Line vline2BackDown = new Line(this.vp2, leftDownCorner);
136 |
137 | PVector backCornerDown = vline1BackDown.intersect(vline2BackDown);
138 | VerticalLine backEdge = new VerticalLine(backCornerUp.x);
139 |
140 | stroke(255, 153, 0);
141 |
142 | strokeWeight(1);
143 | leftEdge.display(leftUpCorner.y, leftDownCorner.y);
144 | rightEdge.display(rightUpCorner.y, rightDownCorner.y);
145 |
146 | vline1BackUp.display();
147 | vline2BackUp.display();
148 |
149 | vline1BackDown.display();
150 | vline2BackDown.display();
151 |
152 | vline1Up.display();
153 | vline2Up.display();
154 |
155 | vline1Down.display();
156 | vline2Down.display();
157 |
158 | frontEdge.display(this.centerCorner.y, downCorner.y);
159 |
160 | strokeWeight(10);
161 | drawVertex(leftUpCorner);
162 | drawVertex(rightUpCorner);
163 | drawVertex(leftDownCorner);
164 | drawVertex(rightDownCorner);
165 | drawVertex(this.vp1);
166 | drawVertex(this.vp2);
167 |
168 | drawVertex(this.centerCorner);
169 | drawVertex(downCorner);
170 |
171 | drawVertex(backCornerUp);
172 |
173 | // Display back semi transparent
174 | stroke(255, 153, 0, 100);
175 |
176 | strokeWeight(10);
177 | drawVertex(backCornerDown);
178 |
179 | strokeWeight(2);
180 | backEdge.display(backCornerUp.y, backCornerDown.y);
181 |
182 | fill(255, 153, 0, 100);
183 | drawQuad(leftUpCorner, backCornerUp, backCornerDown, leftDownCorner);
184 | drawQuad(rightUpCorner, backCornerUp, backCornerDown, rightDownCorner);
185 |
186 | drawQuad(rightUpCorner, this.centerCorner, downCorner, rightDownCorner);
187 | drawQuad(leftUpCorner, this.centerCorner, downCorner, leftDownCorner);
188 |
189 | fill(255, 153, 50, 150);
190 | drawQuad(this.centerCorner, leftUpCorner, backCornerUp, rightUpCorner);
191 | drawQuad(downCorner, leftDownCorner, backCornerDown, rightDownCorner);
192 | }
193 | }
194 |
195 | PerspectiveCube cube;
196 | float offset = 0;
197 |
198 | void setup() {
199 | size(500, 500);
200 |
201 | cube = new PerspectiveCube(50, 150, width - 50, 150, width / 2, height / 2, 150);
202 | }
203 |
204 | void draw() {
205 | background(#003344);
206 |
207 | cube.centerCorner.x = width / 2 + sin(offset / 2) * cos(offset) * 100;
208 | cube.centerCorner.y = height / 2 + sin(offset / 2) * 50;
209 |
210 | cube.vp1.x = 100 + sin(offset / 2) * 50;
211 | cube.vp2.x = height - 100 + cos(offset / 2) * 50;
212 |
213 | cube.vp1.y = height / 2 + sin(-offset) * 100;
214 | cube.vp2.y = height / 2 + cos(-offset) * 100;
215 |
216 | cube.display();
217 |
218 | offset += 0.03;
219 | }
220 |
221 |
--------------------------------------------------------------------------------
/2021/day_26/genuary_26.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_26/genuary_26.gif
--------------------------------------------------------------------------------
/2021/day_27/day_27.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * From: https://easings.net/#easeInExpo
6 | */
7 | function easeOutBounce(x) {
8 | const n1 = 7.5625;
9 | const d1 = 2.75;
10 |
11 | if (x < 1 / d1) {
12 | return n1 * x * x;
13 | } else if (x < 2 / d1) {
14 | return n1 * (x -= 1.5 / d1) * x + 0.75;
15 | } else if (x < 2.5 / d1) {
16 | return n1 * (x -= 2.25 / d1) * x + 0.9375;
17 | } else {
18 | return n1 * (x -= 2.625 / d1) * x + 0.984375;
19 | }
20 | }
21 |
22 | /**
23 | * From: https://p5js.org/reference/#/p5/pixels
24 | */
25 | function setPixel(x, y, r, g, b) {
26 | const d = pixelDensity();
27 |
28 | for (let i = 0; i < d; i++) {
29 | for (let j = 0; j < d; j++) {
30 | const index = 4 * ((y * d + j) * width * d + (x * d + i));
31 | pixels[index] = r;
32 | pixels[index + 1] = g;
33 | pixels[index + 2] = b;
34 | pixels[index + 3] = 255;
35 | }
36 | }
37 | }
38 |
39 | function putPixels(x, y, offset) {
40 | loadPixels();
41 | for (let i = 0; i < 100; i++) {
42 | const rx = int(x + Math.cos(easeOutBounce(random(1)) + offset) * random(200));
43 | const ry = int(y - Math.sin(easeOutBounce(random(1)) + offset) * random(200));
44 |
45 | const p = easeOutBounce(map(rx + ry, 0, width + height, 1, 0));
46 |
47 | const r = 255 - random(p) * 100;
48 | const g = 153 - random(p) * 153;
49 | const b = 0;
50 |
51 | setPixel(rx, ry, r, g, b);
52 | }
53 | updatePixels();
54 | }
55 |
56 | let offset = 0;
57 |
58 | function setup() {
59 | createCanvas(500, 500);
60 |
61 | background("#003344");
62 | }
63 |
64 | function draw() {
65 | putPixels(width / 2, height / 2, offset);
66 |
67 | if (frameCount % 100 == 0) offset += QUARTER_PI;
68 | }
69 |
--------------------------------------------------------------------------------
/2021/day_27/day_27.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * From: https://easings.net/#easeInExpo
6 | */
7 | float easeOutBounce(float x) {
8 | float n1 = 7.5625;
9 | float d1 = 2.75;
10 |
11 | if (x < 1 / d1) {
12 | return n1 * x * x;
13 | } else if (x < 2 / d1) {
14 | return n1 * (x -= 1.5 / d1) * x + 0.75;
15 | } else if (x < 2.5 / d1) {
16 | return n1 * (x -= 2.25 / d1) * x + 0.9375;
17 | } else {
18 | return n1 * (x -= 2.625 / d1) * x + 0.984375;
19 | }
20 | }
21 |
22 | /**
23 | * From: https://p5js.org/reference/#/p5/pixels
24 | */
25 | void setPixel(int x, int y, int r, int g, int b) {
26 | int loc = x + y * width;
27 | pixels[loc] = color(r, g, b);
28 | }
29 |
30 | void putPixels(float x, float y, float offset) {
31 | loadPixels();
32 | for (int i = 0; i < 120; i++) {
33 | int rx = int(x + cos(easeOutBounce(random(1)) + offset) * random(200));
34 | int ry = int(y - sin(easeOutBounce(random(1)) + offset) * random(200));
35 |
36 | float p = easeOutBounce(map(rx + ry, 0, width + height, 1, 0));
37 |
38 | float r = 255 - random(p) * 100;
39 | float g = 153 - random(p) * 153;
40 | float b = 0;
41 |
42 | setPixel(rx, ry, int(r), int(g), int(b));
43 | }
44 | updatePixels();
45 | }
46 |
47 | float offset = 0;
48 |
49 | void setup() {
50 | size(500, 500);
51 |
52 | background(#003344);
53 | }
54 |
55 | void draw() {
56 | putPixels(width / 2, height / 2, offset);
57 |
58 | if (frameCount % 80 == 0) offset += QUARTER_PI;
59 | }
60 |
--------------------------------------------------------------------------------
/2021/day_27/genuary_27.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_27/genuary_27.gif
--------------------------------------------------------------------------------
/2021/day_28/day_28.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | const margin = 70;
5 |
6 | /**
7 | * Here a particle is a sound particle
8 | * it has a direction, a speed and a source point
9 | */
10 | class Particle {
11 | constructor(x, y, direction, speed) {
12 | this.location = createVector(x, y);
13 | this.direction = direction;
14 | this.speed = speed;
15 | }
16 |
17 | /**
18 | * Move the particle and handle wall collisions
19 | */
20 | update() {
21 | this.location.add(p5.Vector.mult(this.direction, this.speed));
22 |
23 | const limit = width / 2 - margin;
24 |
25 | // Touch left or right wall
26 | if (this.location.x < -limit || this.location.x > limit) {
27 | this.direction.x *= -1;
28 | }
29 |
30 | // Touch top or bottom wall
31 | if (this.location.y < -limit || this.location.y > limit) {
32 | this.direction.y *= -1;
33 | }
34 | }
35 |
36 | /**
37 | * Display the particle as a dot
38 | */
39 | display() {
40 | stroke(255, 80);
41 | strokeWeight(5);
42 | point(this.location.x, this.location.y);
43 | }
44 | }
45 |
46 | /**
47 | * A sound wave is composed of multiple sound particles
48 | */
49 | class Wave {
50 | constructor(x, y, nParticles, speed) {
51 | this.x = x;
52 | this.y = y;
53 | this.particles = [];
54 |
55 | // Create the particles from the center of the wave
56 | for (let i = 0; i < nParticles; i++) {
57 | const angle = i * (TWO_PI / nParticles);
58 | const direction = p5.Vector.fromAngle(angle);
59 |
60 | this.particles.push(new Particle(this.x, this.y, direction, speed));
61 | }
62 | }
63 |
64 | /**
65 | * Update all the particles
66 | */
67 | update() {
68 | for (const particle of this.particles) {
69 | particle.update();
70 | }
71 | }
72 |
73 | /**
74 | * Display the wave as the line between all the particles
75 | */
76 | display() {
77 | beginShape();
78 |
79 | for (const particle of this.particles) {
80 | particle.display();
81 | vertex(particle.location.x, particle.location.y);
82 | }
83 |
84 | strokeWeight(2);
85 | stroke(255, 153, 0, 60);
86 | noFill();
87 |
88 | endShape(CLOSE);
89 | }
90 | }
91 |
92 | /**
93 | * A sound speaker is an object that emit sound waves
94 | * It has a constant rate
95 | */
96 | class SoundSpeaker {
97 | constructor(w, h, rate) {
98 | this.w = w;
99 | this.h = h;
100 | this.rate = rate;
101 | this.time = millis();
102 |
103 | this.waves = [];
104 |
105 | this.emitWaves();
106 | }
107 |
108 | /**
109 | * Emit one wave on each of the speakers
110 | */
111 | emitWaves() {
112 | const nParticles = 200;
113 | const speed = random(0.5, 2);
114 |
115 | this.waves.push(new Wave(0, this.h / 5, nParticles, speed));
116 |
117 | this.waves.push(new Wave(0, -this.h / 5, nParticles / 2, speed / 2));
118 | }
119 |
120 | /**
121 | * Emit at a constant rate and update waves
122 | */
123 | update() {
124 | // Emit sound wave every rate time
125 | if (millis() - this.time > this.rate) {
126 | this.emitWaves();
127 | this.time = millis();
128 | }
129 |
130 | // Update the waves
131 | for (const wave of this.waves) {
132 | wave.update();
133 | }
134 | }
135 |
136 | /**
137 | * Display a simplified speaker and the waves
138 | */
139 | display() {
140 | push();
141 | translate(width / 2, height / 2);
142 |
143 | noFill();
144 | stroke(255, 153, 0);
145 | strokeWeight(3);
146 | rectMode(CENTER);
147 |
148 | rect(0, 0, this.w, this.h, 5);
149 |
150 | strokeWeight(2);
151 |
152 | circle(0, this.h / 5, this.w * 0.7);
153 | circle(0, -this.h / 5, this.w * 0.4);
154 |
155 | fill(255, 153, 0);
156 | circle(0, this.h / 5, this.w * 0.2);
157 | circle(0, -this.h / 5, this.w * 0.1);
158 |
159 | for (const wave of this.waves) {
160 | wave.display();
161 | }
162 |
163 | pop();
164 | }
165 | }
166 |
167 | let speaker;
168 |
169 | function setup() {
170 | createCanvas(500, 500);
171 |
172 | speaker = new SoundSpeaker(70, 140, 5000);
173 | }
174 |
175 | function draw() {
176 | background("#003344");
177 |
178 | speaker.display();
179 | speaker.update();
180 | }
181 |
182 |
--------------------------------------------------------------------------------
/2021/day_28/day_28.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | final float margin = 70;
7 |
8 | /**
9 | * Here a particle is a sound particle
10 | * it has a direction, a speed and a source point
11 | */
12 | class Particle {
13 | PVector location, direction;
14 | float speed;
15 |
16 | Particle(float x, float y, PVector direction, float speed) {
17 | this.location = new PVector(x, y);
18 | this.direction = direction;
19 | this.speed = speed;
20 | }
21 |
22 | /**
23 | * Move the particle and handle wall collisions
24 | */
25 | void update() {
26 | this.location.add(PVector.mult(this.direction, this.speed));
27 |
28 | float limit = width / 2 - margin;
29 |
30 | // Touch left or right wall
31 | if (this.location.x < -limit || this.location.x > limit) {
32 | this.direction.x *= -1;
33 | }
34 |
35 | // Touch top or bottom wall
36 | if (this.location.y < -limit || this.location.y > limit) {
37 | this.direction.y *= -1;
38 | }
39 | }
40 |
41 | /**
42 | * Display the particle as a dot
43 | */
44 | void display() {
45 | stroke(255, 80);
46 | strokeWeight(5);
47 | point(this.location.x, this.location.y);
48 | }
49 | }
50 |
51 | /**
52 | * A sound wave is composed of multiple sound particles
53 | */
54 | class Wave {
55 | float x, y;
56 | List particles;
57 |
58 | Wave(float x, float y, int nParticles, float speed) {
59 | this.x = x;
60 | this.y = y;
61 | this.particles = new ArrayList();
62 |
63 | // Create the particles from the center of the wave
64 | for (int i = 0; i < nParticles; i++) {
65 | float angle = i * (TWO_PI / nParticles);
66 | PVector direction = PVector.fromAngle(angle);
67 |
68 | this.particles.add(new Particle(this.x, this.y, direction, speed));
69 | }
70 | }
71 |
72 | /**
73 | * Update all the particles
74 | */
75 | void update() {
76 | for (Particle particle : this.particles) {
77 | particle.update();
78 | }
79 | }
80 |
81 | /**
82 | * Display the wave as the line between all the particles
83 | */
84 | void display() {
85 | beginShape();
86 |
87 | for (Particle particle : this.particles) {
88 | particle.display();
89 | vertex(particle.location.x, particle.location.y);
90 | }
91 |
92 | strokeWeight(2);
93 | stroke(255, 153, 0, 60);
94 | noFill();
95 |
96 | endShape(CLOSE);
97 | }
98 | }
99 |
100 | /**
101 | * A sound speaker is an object that emit sound waves
102 | * It has a constant rate
103 | */
104 | class SoundSpeaker {
105 | float w, h;
106 | int rate, time;
107 | List waves;
108 |
109 | SoundSpeaker(float w, float h, int rate) {
110 | this.w = w;
111 | this.h = h;
112 | this.rate = rate;
113 | this.time = millis();
114 |
115 | this.waves = new ArrayList();
116 |
117 | this.emitWaves();
118 | }
119 |
120 | /**
121 | * Emit one wave on each of the speakers
122 | */
123 | void emitWaves() {
124 | int nParticles = 200;
125 | float speed = random(0.5, 2);
126 |
127 | this.waves.add(new Wave(0, this.h / 5, nParticles, speed));
128 |
129 | this.waves.add(new Wave(0, -this.h / 5, nParticles / 2, speed / 2));
130 | }
131 |
132 | /**
133 | * Emit at a constant rate and update waves
134 | */
135 | void update() {
136 | // Emit sound wave every rate time
137 | if (millis() - this.time > this.rate) {
138 | this.emitWaves();
139 | this.time = millis();
140 | }
141 |
142 | // Update the waves
143 | for (Wave wave : this.waves) {
144 | wave.update();
145 | }
146 | }
147 |
148 | /**
149 | * Display a simplified speaker and the waves
150 | */
151 | void display() {
152 | push();
153 | translate(width / 2, height / 2);
154 |
155 | noFill();
156 | stroke(255, 153, 0);
157 | strokeWeight(3);
158 | rectMode(CENTER);
159 |
160 | rect(0, 0, this.w, this.h, 5);
161 |
162 | strokeWeight(2);
163 |
164 | circle(0, this.h / 5, this.w * 0.7);
165 | circle(0, -this.h / 5, this.w * 0.4);
166 |
167 | fill(255, 153, 0);
168 | circle(0, this.h / 5, this.w * 0.2);
169 | circle(0, -this.h / 5, this.w * 0.1);
170 |
171 | for (Wave wave : this.waves) {
172 | wave.display();
173 | }
174 |
175 | pop();
176 | }
177 | }
178 |
179 | SoundSpeaker speaker;
180 |
181 | void setup() {
182 | size(500, 500);
183 |
184 | speaker = new SoundSpeaker(70, 140, 1000);
185 | }
186 |
187 | void draw() {
188 | background(#003344);
189 |
190 | speaker.display();
191 | speaker.update();
192 | }
193 |
--------------------------------------------------------------------------------
/2021/day_28/genuary_28.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_28/genuary_28.gif
--------------------------------------------------------------------------------
/2021/day_29/day_29.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | function band(x, y, w, h, gap, nBands, offset) {
5 | push();
6 | translate(x, y);
7 |
8 | fill("#ff9900");
9 | noStroke();
10 |
11 | const size = h - ((nBands - 1) * gap);
12 | let ratio = 0;
13 |
14 | for (let i = 0; i < nBands; i++) {
15 | let pieceRatio;
16 |
17 | if (i == nBands - 1) {
18 | pieceRatio = 1 - ratio;
19 | } else {
20 | pieceRatio = noise(ratio / 5 + offset + i * 0.5) * (1 - ratio) * 0.9;
21 | }
22 |
23 | const yy = ratio * size + i * gap;
24 |
25 | rect(0, yy, w, pieceRatio * size);
26 |
27 | ratio += pieceRatio;
28 | }
29 |
30 | pop();
31 | }
32 |
33 | const margin = 70;
34 | const nBands = 5;
35 | const gap = 10;
36 | let bandSize;
37 | let bandWidth;
38 | let offset = 0;
39 |
40 | function setup() {
41 | createCanvas(500, 500);
42 |
43 | // The size of the band
44 | bandSize = width - 2 * margin;
45 |
46 | // The thickness of each band, considering the gaps
47 | bandWidth = (bandSize - ((nBands - 1) * gap)) / nBands;
48 | }
49 |
50 | function draw() {
51 | background("#003344");
52 |
53 | // Draw the bands
54 | for (let i = 0; i < nBands; i++) {
55 | const x = margin + i * (bandWidth + gap);
56 | const bandOffset = cos(TWO_PI + offset + i * 5) / 5;
57 |
58 | band(x, margin, bandWidth, bandSize, gap, 6, bandOffset);
59 | }
60 |
61 | offset += 0.05;
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/2021/day_29/day_29.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | void band(float x, float y, float w, float h, float gap, int nBands, float offset) {
5 | push();
6 | translate(x, y);
7 |
8 | fill(#ff9900);
9 | noStroke();
10 |
11 | float size = h - ((nBands - 1) * gap);
12 | float ratio = 0;
13 |
14 | for (int i = 0; i < nBands; i++) {
15 | float pieceRatio;
16 |
17 | if (i == nBands - 1) {
18 | pieceRatio = 1 - ratio;
19 | } else {
20 | pieceRatio = noise(ratio / 5 + offset + i * 0.5) * (1 - ratio) * 0.9;
21 | }
22 |
23 | float yy = ratio * size + i * gap;
24 |
25 | rect(0, yy, w, pieceRatio * size);
26 |
27 | ratio += pieceRatio;
28 | }
29 |
30 | pop();
31 | }
32 |
33 | final float margin = 70;
34 | final int nBands = 5;
35 | final float gap = 10;
36 | float bandSize;
37 | float bandWidth;
38 | float offset = 0;
39 |
40 | void setup() {
41 | size(500, 500);
42 |
43 | // The size of the band
44 | bandSize = width - 2 * margin;
45 |
46 | // The thickness of each band, considering the gaps
47 | bandWidth = (bandSize - ((nBands - 1) * gap)) / nBands;
48 | }
49 |
50 | void draw() {
51 | background(#003344);
52 |
53 | // Draw the bands
54 | for (int i = 0; i < nBands; i++) {
55 | float x = margin + i * (bandWidth + gap);
56 | float bandOffset = cos(TWO_PI + offset + i * 5) / 5;
57 |
58 | band(x, margin, bandWidth, bandSize, gap, 6, bandOffset);
59 | }
60 |
61 | offset += 0.05;
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/2021/day_29/genuary_29.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_29/genuary_29.gif
--------------------------------------------------------------------------------
/2021/day_3/day_3.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Stadium shape
6 | * See : https://en.wikipedia.org/wiki/Stadium_(geometry)
7 | */
8 | function stadium(x, y, width, height, rotation) {
9 | const bodyWidth = (width - height);
10 | const diameter = width - bodyWidth;
11 |
12 | push();
13 | translate(x, y);
14 | rotate(rotation);
15 |
16 | line(-bodyWidth / 2, -height / 2, bodyWidth / 2, -height / 2);
17 | line(-bodyWidth / 2, height / 2, bodyWidth / 2, height / 2);
18 |
19 | arc(-bodyWidth / 2, 0, diameter, diameter, HALF_PI, PI + HALF_PI);
20 | arc(bodyWidth / 2, 0, diameter, diameter, -HALF_PI, HALF_PI);
21 |
22 | pop();
23 | }
24 |
25 | /**
26 | * Prodecural hand (too much proceduralism... ;)
27 | */
28 | function hand(x, y, width, height, rotation, fingersRatio, thumbRatio, palmRatio) {
29 | const totalFingersHeight = height / 2;
30 | const nFingers = fingersRatio.length;
31 |
32 | const fingersGap = width / 10;
33 | const fingersWidth = (width - fingersGap * (nFingers - 1)) / nFingers;
34 |
35 | const fingerStartX = -width / 2 + fingersWidth / 2;
36 |
37 | // style
38 | noFill();
39 | stroke("#ff9900");
40 | strokeWeight(2);
41 |
42 | // transform
43 | push();
44 | translate(x, y);
45 | rotate(rotation);
46 |
47 | // fingers
48 | for (let i = 0; i < fingersRatio.length; i++) {
49 | const fingerHeight = fingersRatio[i] * totalFingersHeight;
50 | const yOffset = (fingerHeight - fingersWidth) / 2;
51 | stadium(fingerStartX + i * (fingersWidth + fingersGap), -yOffset, fingerHeight, fingersWidth, HALF_PI);
52 | }
53 |
54 | // hand palm
55 | const palmHeight = height * palmRatio;
56 | const palmOffsetY = palmHeight / 2 + fingersGap * 2;
57 | stadium(0, palmOffsetY, palmHeight, width * 0.4, HALF_PI);
58 |
59 | // thumb
60 | const thumbHeight = thumbRatio * totalFingersHeight;
61 | const thumbOffset = thumbHeight / (2 * Math.sqrt(2));
62 | stadium(-width / 2 - thumbOffset, palmOffsetY - width / 2 - thumbOffset, thumbHeight, fingersWidth, QUARTER_PI);
63 |
64 | pop();
65 | }
66 |
67 | const margin = 60;
68 | const nHands = 4;
69 | let offset = 0;
70 |
71 | const defaultFingersRatio = [0.8, 1, 0.9, 0.6];
72 |
73 | function setup() {
74 | createCanvas(500, 500);
75 | }
76 |
77 | function draw() {
78 | background("#003344");
79 |
80 | const maxHandSize = (width - 2 * margin) / nHands;
81 |
82 | for (let i = 0; i < nHands; i++) {
83 | const x = margin + maxHandSize / 2 + i * maxHandSize;
84 | for (let j = 0; j < nHands; j++) {
85 | const noiseOffset = (i + j) / 2;
86 |
87 | const fingersRatio = defaultFingersRatio.map((ratio, index) => {
88 | return noise(offset + (i + j + index) * 100) * ratio;
89 | });
90 |
91 | const thumbRatio = noise(offset + noiseOffset);
92 | const palmRatio = noise(offset - noiseOffset);
93 |
94 | const y = margin + maxHandSize / 2 + j * maxHandSize;
95 | const rotation = QUARTER_PI * ((noise(offset + noiseOffset * 2) * 2) - 1);
96 |
97 | hand(x, y, maxHandSize * (noise(offset) + 0.1), maxHandSize, rotation, fingersRatio, thumbRatio, palmRatio);
98 | }
99 | }
100 |
101 | offset += 0.05;
102 | }
103 |
--------------------------------------------------------------------------------
/2021/day_3/day_3.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Stadium shape
6 | * See : https://en.wikipedia.org/wiki/Stadium_(geometry)
7 | */
8 | void stadium(float x, float y, float width, float height, float rotation) {
9 | float bodyWidth = (width - height);
10 | float diameter = width - bodyWidth;
11 |
12 | push();
13 | translate(x, y);
14 | rotate(rotation);
15 |
16 | line(-bodyWidth / 2, -height / 2, bodyWidth / 2, -height / 2);
17 | line(-bodyWidth / 2, height / 2, bodyWidth / 2, height / 2);
18 |
19 | arc(-bodyWidth / 2, 0, diameter, diameter, HALF_PI, PI + HALF_PI);
20 | arc(bodyWidth / 2, 0, diameter, diameter, -HALF_PI, HALF_PI);
21 |
22 | pop();
23 | }
24 |
25 | /**
26 | * Prodecural hand (too much proceduralism... ;)
27 | */
28 | void hand(float x, float y, float width, float height, float rotation, float fingersRatio[], float thumbRatio, float palmRatio) {
29 | float totalFingersHeight = height / 2;
30 | float nFingers = fingersRatio.length;
31 |
32 | float fingersGap = width / 10;
33 | float fingersWidth = (width - fingersGap * (nFingers - 1)) / nFingers;
34 |
35 | float fingerStartX = -width / 2 + fingersWidth / 2;
36 |
37 | // style
38 | noFill();
39 | stroke(#ff9900);
40 | strokeWeight(2);
41 |
42 | // transform
43 | push();
44 | translate(x, y);
45 | rotate(rotation);
46 |
47 | // fingers
48 | for (int i = 0; i < fingersRatio.length; i++) {
49 | float fingerHeight = fingersRatio[i] * totalFingersHeight;
50 | float yOffset = (fingerHeight - fingersWidth) / 2;
51 | stadium(fingerStartX + i * (fingersWidth + fingersGap), -yOffset, fingerHeight, fingersWidth, HALF_PI);
52 | }
53 |
54 | // hand palm
55 | float palmHeight = height * palmRatio;
56 | float palmOffsetY = palmHeight / 2 + fingersGap * 2;
57 | stadium(0, palmOffsetY, palmHeight, width * 0.4, HALF_PI);
58 |
59 | // thumb
60 | float thumbHeight = thumbRatio * totalFingersHeight;
61 | float thumbOffset = thumbHeight / (2 * sqrt(2));
62 | stadium(-width / 2 - thumbOffset, palmOffsetY - width / 2 - thumbOffset, thumbHeight, fingersWidth, QUARTER_PI);
63 |
64 | pop();
65 | }
66 |
67 | float margin = 60;
68 | float nHands = 4;
69 | float offset = 0;
70 |
71 | float[] defaultFingersRatio = {0.8, 1, 0.9, 0.6};
72 |
73 | void setup() {
74 | size(500, 500);
75 | }
76 |
77 | void draw() {
78 | background(#003344);
79 |
80 |
81 |
82 | float maxHandSize = (width - 2 * margin) / nHands;
83 |
84 | for (int i = 0; i < nHands; i++) {
85 | float x = margin + maxHandSize / 2 + i * maxHandSize;
86 | for (int j = 0; j < nHands; j++) {
87 | float noiseOffset = (i + j) / 2;
88 |
89 | float fingersRatio[] = new float[4];
90 | for (int index = 0; index < fingersRatio.length; index++) {
91 | fingersRatio[index] = noise(offset + (i + j + index) * 100) * defaultFingersRatio[index];
92 | }
93 |
94 | float thumbRatio = noise(offset + noiseOffset);
95 | float palmRatio = noise(offset - noiseOffset);
96 |
97 | float y = margin + maxHandSize / 2 + j * maxHandSize;
98 | float rotation = QUARTER_PI * ((noise(offset + noiseOffset * 2) * 2) - 1);
99 |
100 | hand(x, y, maxHandSize * (noise(offset) + 0.1), maxHandSize, rotation, fingersRatio, thumbRatio, palmRatio);
101 | }
102 | }
103 |
104 | offset += 0.05;
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/2021/day_3/genuary_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_3/genuary_3.gif
--------------------------------------------------------------------------------
/2021/day_30/day_30.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Metaball class
6 | * See : https://en.wikipedia.org/wiki/Metaballs
7 | */
8 | class MetaBall {
9 | constructor(x, y, diameter) {
10 | this.location = createVector(x, y);
11 | this.diameter = diameter;
12 | }
13 |
14 | /**
15 | * Inverse square law function to evaluate the metaball
16 | */
17 | evaluate(x, y) {
18 | const distSq = pow(this.location.x - x, 2) + pow(this.location.y - y, 2);
19 |
20 | if (distSq == 0) {
21 | return 0;
22 | }
23 |
24 | if (distSq < pow(this.diameter, 2)) {
25 | if (1 / distSq == 0) console.log("zero");
26 | return 1 / distSq;
27 |
28 | }
29 |
30 | return 0;
31 | }
32 | }
33 |
34 | /**
35 | * Evaluate the scalar at that pixel considering a list of metaballs
36 | */
37 | function evaluatePixel(x, y, metaballs) {
38 | let sum = 0;
39 |
40 | for (const metaball of metaballs) {
41 | sum += metaball.evaluate(x, y);
42 | }
43 |
44 | return sum;
45 | }
46 |
47 | /**
48 | * From: https://p5js.org/reference/#/p5/pixels
49 | */
50 | function setPixel(x, y, r, g, b) {
51 | const d = pixelDensity();
52 |
53 | for (let i = 0; i < d; i++) {
54 | for (let j = 0; j < d; j++) {
55 | const index = 4 * ((y * d + j) * width * d + (x * d + i));
56 | pixels[index] = r;
57 | pixels[index + 1] = g;
58 | pixels[index + 2] = b;
59 | pixels[index + 3] = 255;
60 | }
61 | }
62 | }
63 |
64 | const metaballs = [];
65 | const nBalls = 10;
66 |
67 | function setup() {
68 | createCanvas(500, 500);
69 |
70 | for (let i = 0; i < nBalls; i++) {
71 | const diameter = 100;
72 | const radius = diameter / 2;
73 |
74 | const x = int(random(radius, width - radius));
75 | const y = int(random(radius, height - radius));
76 |
77 | metaballs.push(new MetaBall(x, y, diameter));
78 | }
79 | }
80 |
81 | function draw() {
82 | background("#003344");
83 |
84 | loadPixels();
85 | for (let x = 0; x < width; x++) {
86 | for (let y = 0; y < height; y++) {
87 | const value = evaluatePixel(x, y, metaballs);
88 |
89 | if (value > 0 && value < 0.01) {
90 | setPixel(x, y, 255, 153, 0);
91 | }
92 | }
93 | }
94 | updatePixels();
95 |
96 | noLoop();
97 | }
98 |
99 |
100 |
--------------------------------------------------------------------------------
/2021/day_4/day_4.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Bubble shape (like conversation icon)
6 | */
7 | function bubble(x, y, size, rotation) {
8 | noFill();
9 | stroke(255);
10 | strokeWeight(5);
11 |
12 | push();
13 | translate(x, y);
14 | rotate(rotation);
15 |
16 | arc(0, 0, size, size, HALF_PI, 0);
17 |
18 | const half = size / 2;
19 |
20 | line(half, 0, half, half);
21 | line(0, half, half, half);
22 |
23 | pop();
24 | }
25 |
26 | /**
27 | * Rotating bubbles with gap
28 | */
29 | function bubbles(x, y, size, rotation, offset) {
30 | const gap = (cos(offset) + 2) * 20;
31 |
32 | let i = 0;
33 | for (let radius = size; radius > 0; radius -= gap) {
34 | const rotationOffset = sin(offset * 2 + i * 0.1) * TWO_PI;
35 | bubble(x, y, radius, rotation + rotationOffset);
36 | i++;
37 | }
38 | }
39 |
40 | // Size of the bubbles
41 | const size = 150;
42 | const gap = 10;
43 |
44 | // Animation offset
45 | let offset = 0;
46 |
47 | function setup() {
48 | createCanvas(500, 500);
49 | }
50 |
51 | function draw() {
52 | background("#003344");
53 |
54 | translate(width / 2, height / 2);
55 |
56 | const bOffset = gap + size / 2;
57 |
58 | // Symmetry is here!
59 | bubbles(-bOffset, -bOffset, size, 0, offset);
60 | bubbles(bOffset, -bOffset, size, HALF_PI, offset);
61 | bubbles(-bOffset, bOffset, size, -HALF_PI, offset);
62 | bubbles(bOffset, bOffset, size, PI, offset);
63 |
64 | offset += 0.01;
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/2021/day_4/day_4.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | /**
5 | * Bubble shape (like conversation icon)
6 | */
7 | void bubble(float x, float y, float size, float rotation) {
8 | noFill();
9 | stroke(255);
10 | strokeWeight(5);
11 |
12 | push();
13 | translate(x, y);
14 | rotate(rotation);
15 |
16 | arc(0, 0, size, size, HALF_PI, TWO_PI);
17 |
18 | float half = size / 2.0;
19 |
20 | line(half, 0, half, half);
21 | line(0, half, half, half);
22 |
23 | pop();
24 | }
25 |
26 | /**
27 | * Rotating bubbles with gap
28 | */
29 | void bubbles(float x, float y, float size, float rotation, float offset) {
30 | float gap = (cos(offset) + 2) * 20;
31 |
32 | int i = 0;
33 | for (float radius = size; radius > 0; radius -= gap) {
34 | float rotationOffset = sin(offset * 2 + i * 0.1) * TWO_PI;
35 | bubble(x, y, radius, rotation + rotationOffset);
36 | i++;
37 | }
38 | }
39 |
40 | // Size of the bubbles
41 | float size = 150;
42 | float gap = 10;
43 |
44 | // Animation offset
45 | float offset = 0;
46 |
47 | void setup() {
48 | size(500, 500);
49 | }
50 |
51 | void draw() {
52 | background(#003344);
53 |
54 | translate(width / 2, height / 2);
55 |
56 | float bOffset = gap + size / 2;
57 |
58 | // Symmetry is here!
59 | bubbles(-bOffset, -bOffset, size, 0, offset);
60 | bubbles(bOffset, -bOffset, size, HALF_PI, offset);
61 | bubbles(-bOffset, bOffset, size, -HALF_PI, offset);
62 | bubbles(bOffset, bOffset, size, PI, offset);
63 |
64 | offset += 0.01;
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/2021/day_4/genuary_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_4/genuary_4.gif
--------------------------------------------------------------------------------
/2021/day_5/day_5.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | setup=_=>{createCanvas(s=500,s);h=s/2};o=0;draw=_=>{background("#003344");rectMode(CENTER);noFill();stroke(255,150);y=(l)=>{push();rotate(cos(o/2+l*0.1));square(0,0,l);pop()};translate(h,h);for(t=0;t= height - margin && lastX == 0) lastX = x;
63 | }
64 | endShape();
65 |
66 | pen(lastX, height - margin, 20, 120, -cos(offset) * QUARTER_PI);
67 | }
68 |
69 | let offset = 0;
70 |
71 | function setup() {
72 | createCanvas(500, 500);
73 | }
74 |
75 | function draw() {
76 | background("#003344");
77 |
78 | penOnPaper(offset);
79 |
80 | offset += 0.04;
81 | }
82 |
--------------------------------------------------------------------------------
/2021/day_7/day_7.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | final float margin = 100;
5 |
6 | /**
7 | * Draw a pen from the tip with rotation
8 | */
9 | void pen(float x, float y, float width , float height, float rotation) {
10 | // The ratio of the different parts compared to the total height
11 | float ratios[] = {0.12, 0.28, 0.5, 0.1};
12 |
13 | push();
14 | translate(x, y);
15 | rotate(rotation);
16 |
17 | strokeJoin(ROUND);
18 | fill(#003344);
19 | stroke(#ff9900);
20 | strokeWeight(4);
21 |
22 | // Tip
23 | float h = width / 2;
24 | float spikeHeight = ratios[0] * height;
25 | triangle(0, 0, -h, -spikeHeight, h, -spikeHeight);
26 |
27 | // Body
28 | float bodyHeight = (ratios[1] + ratios[2]) * height;
29 | float lineHeight = spikeHeight + ratios[1] * height;
30 | line(-h, -lineHeight, h, -lineHeight);
31 | rect(-h, -(spikeHeight + bodyHeight), width, bodyHeight);
32 |
33 | // Eraser
34 | arc(0, -((1 - ratios[3]) * height), width, width, PI, TWO_PI);
35 |
36 | pop();
37 | }
38 |
39 | /**
40 | * Display the pen and the stroke
41 | */
42 | void penOnPaper(float offset) {
43 | float off = 0.1;
44 |
45 | noFill();
46 | stroke(255);
47 | strokeWeight(3);
48 |
49 | float w = (width - 2 * margin) / 2;
50 | float lastX = 0;
51 |
52 | beginShape();
53 | for (float y = margin; y < height - margin; y += off) {
54 | float t = map(y, margin, height - margin, 0, 1);
55 | float angle = log(t + 0.05) * 15;
56 | float x = cos(angle + offset) * w + width / 2;
57 |
58 | curveVertex(x, y);
59 | off += 0.05;
60 |
61 | if (y + off * 2 >= height - margin && lastX == 0) lastX = x;
62 | }
63 | endShape();
64 |
65 | pen(lastX, height - margin, 20, 120, -cos(offset) * QUARTER_PI);
66 | }
67 |
68 | float offset = 0;
69 |
70 | void setup() {
71 | size(500, 500);
72 | }
73 |
74 | void draw() {
75 | background(#003344);
76 |
77 | penOnPaper(offset);
78 |
79 | offset += 0.04;
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/2021/day_7/genuary_7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_7/genuary_7.gif
--------------------------------------------------------------------------------
/2021/day_8/day_8.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | class AnchorPoint {
5 | constructor(x, y, rotation, length) {
6 | this.x = x;
7 | this.y = y;
8 | this.rotation = rotation;
9 | this.length = length;
10 | }
11 |
12 | getControlX() {
13 | return this.x + cos(this.rotation) * this.length;
14 | }
15 |
16 | getControlY() {
17 | return this.y + sin(this.rotation) * this.length;
18 | }
19 |
20 | display() {
21 | stroke("#ff9900");
22 | strokeWeight(4);
23 | line(this.getControlX(), this.getControlY(), this.x, this.y);
24 |
25 | strokeWeight(10);
26 | stroke(255);
27 | point(this.x, this.y);
28 | }
29 | }
30 |
31 | class BezierCurve {
32 | constructor() {
33 | this.anchorPoints = [];
34 | }
35 |
36 | addPoint(anchorPoint) {
37 | this.anchorPoints.push(anchorPoint);
38 | }
39 |
40 | display(offset) {
41 | for (let i = 0; i < this.anchorPoints.length - 1; i++) {
42 | const pt = this.anchorPoints[i];
43 | const nextPt = this.anchorPoints[i + 1];
44 |
45 | stroke(255, 200);
46 | strokeWeight(2);
47 | noFill();
48 | bezier(pt.x, pt.y, pt.getControlX(), pt.getControlY(), nextPt.x, nextPt.y, nextPt.getControlX(), nextPt.getControlY())
49 |
50 | pt.rotation = cos(i * 0.1 + offset) * TWO_PI;
51 | pt.display();
52 | }
53 | }
54 | }
55 |
56 | function randomCurve(nPoints) {
57 | const curve = new BezierCurve();
58 | const margin = 50;
59 |
60 | for (let i = 0; i < nPoints; i++) {
61 | const x = random(margin, width - margin);
62 | const y = random(margin, height - margin);
63 | const length = random(5, 50);
64 | const rotation = random(TWO_PI);
65 |
66 | curve.addPoint(new AnchorPoint(x, y, rotation, length));
67 | }
68 |
69 | return curve;
70 | }
71 |
72 | let curve;
73 |
74 | function setup() {
75 | createCanvas(500, 500);
76 |
77 | curve = randomCurve(20);
78 | }
79 |
80 | function draw() {
81 | background("#003344");
82 |
83 | curve.display();
84 |
85 | //noLoop();
86 | }
87 |
--------------------------------------------------------------------------------
/2021/day_8/day_8.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * Anchor point of a bezier curve
8 | * It has a location and describe the control point
9 | * with polar coordinates
10 | */
11 | class AnchorPoint {
12 | float x, y, rotation, length;
13 |
14 | AnchorPoint(float x, float y, float rotation, float length) {
15 | this.x = x;
16 | this.y = y;
17 | this.rotation = rotation;
18 | this.length = length;
19 | }
20 |
21 | /**
22 | * Return x coordinate of control point
23 | */
24 | float getControlX() {
25 | return this.x + cos(this.rotation) * this.length;
26 | }
27 |
28 | /**
29 | * Return y coordinate of control point
30 | */
31 | float getControlY() {
32 | return this.y + sin(this.rotation) * this.length;
33 | }
34 |
35 | void display() {
36 | stroke(#ff9900);
37 | strokeWeight(4);
38 | line(this.getControlX(), this.getControlY(), this.x, this.y);
39 |
40 | strokeWeight(10);
41 | stroke(255);
42 | point(this.x, this.y);
43 | }
44 | }
45 |
46 | /**
47 | * Bezier curve class contains an array of anchor points
48 | */
49 | class BezierCurve {
50 | List anchorPoints;
51 |
52 | BezierCurve() {
53 | this.anchorPoints = new ArrayList();
54 | }
55 |
56 | /**
57 | * Add a new anchor point
58 | */
59 | void addPoint(AnchorPoint anchorPoint) {
60 | this.anchorPoints.add(anchorPoint);
61 | }
62 |
63 | void display(float offset) {
64 | for (int i = 0; i < this.anchorPoints.size() - 1; i++) {
65 | AnchorPoint pt = this.anchorPoints.get(i);
66 | AnchorPoint nextPt = this.anchorPoints.get(i + 1);
67 |
68 | stroke(255, 200);
69 | strokeWeight(2);
70 | noFill();
71 | bezier(pt.x, pt.y, pt.getControlX(), pt.getControlY(), nextPt.x, nextPt.y, nextPt.getControlX(), nextPt.getControlY());
72 |
73 | pt.rotation = offset + i * 0.5;
74 | pt.display();
75 | }
76 |
77 | // Display last point
78 | AnchorPoint last = this.anchorPoints.get(this.anchorPoints.size() - 1);
79 | last.rotation = offset;
80 | last.display();
81 | }
82 | }
83 |
84 | /**
85 | * Return a random bezier curve inside the canvas with a margin
86 | */
87 | BezierCurve randomCurve(int nPoints, float margin) {
88 | BezierCurve curve = new BezierCurve();
89 |
90 | for (int i = 0; i < nPoints; i++) {
91 | float x = random(margin, width - margin);
92 | float y = random(margin, height - margin);
93 | float length = random(10, 50);
94 | float rotation = random(TWO_PI);
95 |
96 | curve.addPoint(new AnchorPoint(x, y, rotation, length));
97 | }
98 |
99 | return curve;
100 | }
101 |
102 | BezierCurve curve;
103 | float offset = 0;
104 |
105 | void setup() {
106 | size(500, 500);
107 |
108 | curve = randomCurve(15, 70);
109 | }
110 |
111 | void draw() {
112 | background(#003344);
113 |
114 | curve.display(offset);
115 |
116 | offset += 0.05;
117 | }
118 |
--------------------------------------------------------------------------------
/2021/day_8/genuary_8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_8/genuary_8.gif
--------------------------------------------------------------------------------
/2021/day_9/day_9.js:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | function circles(x, y, size, nCircles, offset, thick = 5, yellow = false) {
5 | noFill();
6 |
7 | stroke(255, 200);
8 |
9 | for (let i = 0; i < nCircles; i++) {
10 | const factor = (i / nCircles);
11 | const newSize = factor * size;
12 |
13 | strokeWeight(abs(cos(factor * 5 + offset)) * thick + 5)
14 | circle(x + cos(factor - offset) * size / 8, y, newSize);
15 | }
16 | }
17 |
18 | let offset = 0;
19 | const size = 300;
20 |
21 | function setup() {
22 | createCanvas(500, 500);
23 | }
24 |
25 | function draw() {
26 | background("#003344");
27 |
28 | translate(width / 2, height / 2);
29 |
30 | circles(0, 0, size - cos(offset) * 50, 10, offset);
31 | circles(0, 0, size + cos(offset) * 50, 10, offset * 2);
32 |
33 | offset += 0.02;
34 | }
35 |
--------------------------------------------------------------------------------
/2021/day_9/day_9.pde:
--------------------------------------------------------------------------------
1 | // (c) 2021 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | void circles(float x, float y, float size, int nCircles, float offset) {
5 | noFill();
6 |
7 | stroke(255, 200);
8 |
9 | for (int i = 0; i < nCircles; i++) {
10 | float factor = ((float) i / nCircles);
11 | float newSize = factor * size;
12 |
13 | strokeWeight(abs(cos(factor * 5 + offset)) * 5 + 5);
14 | circle(x + cos(factor - offset) * size / 8, y, newSize);
15 | }
16 | }
17 |
18 | float offset = 0;
19 | float size = 300;
20 |
21 | void setup() {
22 | size(500, 500);
23 | }
24 |
25 | void draw() {
26 | background(#003344);
27 |
28 | translate(width / 2, height / 2);
29 |
30 | circles(0, 0, size - cos(offset) * 50, 10, offset);
31 | circles(0, 0, size + cos(offset) * 50, 10, offset * 2);
32 |
33 | offset += 0.02;
34 | }
35 |
--------------------------------------------------------------------------------
/2021/day_9/genuary_9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2021/day_9/genuary_9.gif
--------------------------------------------------------------------------------
/2023/README.md:
--------------------------------------------------------------------------------
1 | ## 2023 edition
2 |
3 | | [](./day_1/day_1.js)
Perfect loop | [](./day_2/day_2.js)
Made in 10 minutes | [](./day_3/day_3.pde)
Glitch Art |
4 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
5 | | [](./day_4/day_4.pde)
Intersections | [](./day_5/day_5.pde)
Debug view | [](./day_6/day_6.js)
Steal Like An Artist (credit: **Vahram Muratyan**) |
6 | | [](./day_7/day_7.js)
Sample a color palette from an album cover | [](./day_8/day_8.pde)
Signed Distance Functions | [](./day_9/day_9.pde)
Plants |
7 | | [](./day_10/day_10.pde)
Generative Music | [](./day_11/day_11.pde)
Suprematism | [](./day_12/day_12.pde)
Tessellation |
8 | | | | |
9 | | | | |
10 | | | | |
11 | | | | |
12 | | | | |
13 | | | | |
14 | | | | |
15 | | | | |
16 |
--------------------------------------------------------------------------------
/2023/day_1/day_1.js:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | function setLineDash(list) {
5 | drawingContext.setLineDash(list);
6 | }
7 |
8 | function easeInOutQuint(x) {
9 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
10 | }
11 |
12 | function easeInOutQuad(x) {
13 | return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
14 | }
15 |
16 | function bezierWithHandles(x1, y1, x2, y2, x3, y3, x4, y4) {
17 | noFill();
18 | stroke("#2f4858");
19 | strokeWeight(2);
20 | bezier(x1, y1, x2, y2, x3, y3, x4, y4);
21 |
22 | setLineDash([4, 2, 4]);
23 | strokeWeight(2);
24 | stroke("#df8b4a");
25 | line(x1, y1, x2, y2);
26 | line(x3, y3, x4, y4);
27 | setLineDash([]);
28 |
29 | strokeWeight(10);
30 | stroke("#4a5577");
31 | point(x1, y1);
32 | point(x4, y4);
33 |
34 | strokeWeight(4);
35 | stroke("#b15f83");
36 | fill("#dfe0df");
37 | circle(x2, y2, 6);
38 | circle(x3, y3, 6);
39 | }
40 |
41 | function infiniteLoop(x, y, radius, space, roundFactor) {
42 | push();
43 | translate(x, y);
44 |
45 | const cy = space / 2 + radius;
46 | const cyDist = cy * 2 * roundFactor;
47 | const bxOff = 0;
48 |
49 | // Circle dashed contour
50 | setLineDash([2, 4]);
51 | stroke("#9eb0a2");
52 | fill("#92BCD744");
53 | circle(0, -cy, radius * 2);
54 | circle(0, cy, radius * 2);
55 | setLineDash([]);
56 |
57 | // Middle
58 | bezierWithHandles(
59 | bxOff - radius,
60 | cy,
61 | bxOff - radius,
62 | cy - cyDist,
63 | radius,
64 | -cy + cyDist,
65 | radius,
66 | -cy
67 | );
68 |
69 | bezierWithHandles(
70 | bxOff + radius,
71 | cy,
72 | bxOff + radius,
73 | cy - cyDist,
74 | -radius,
75 | -cy + cyDist,
76 | -radius,
77 | -cy
78 | );
79 |
80 | const loopRadius = radius * roundFactor;
81 |
82 | // Bottom loop
83 | bezierWithHandles(
84 | bxOff + radius,
85 | cy,
86 | bxOff + radius,
87 | cy + loopRadius,
88 | bxOff + loopRadius,
89 | cy + radius,
90 | bxOff,
91 | cy + radius
92 | );
93 |
94 | bezierWithHandles(
95 | bxOff - radius,
96 | cy,
97 | bxOff - radius,
98 | cy + loopRadius,
99 | bxOff - loopRadius,
100 | cy + radius,
101 | bxOff,
102 | cy + radius
103 | );
104 |
105 | // Top loop
106 | bezierWithHandles(
107 | radius,
108 | -cy,
109 | radius,
110 | -cy - loopRadius,
111 | loopRadius,
112 | -cy - radius,
113 | 0,
114 | -cy - radius
115 | );
116 |
117 | bezierWithHandles(
118 | -radius,
119 | -cy,
120 | -radius,
121 | -cy - loopRadius,
122 | -loopRadius,
123 | -cy - radius,
124 | 0,
125 | -cy - radius
126 | );
127 |
128 | // Circle center points
129 | strokeWeight(5);
130 | stroke("#4a5577");
131 | point(0, -cy);
132 | point(0, cy);
133 |
134 | pop();
135 | }
136 |
137 | let timeValue = 0;
138 |
139 | function setup() {
140 | createCanvas(400, 400);
141 | }
142 |
143 | function draw() {
144 | background("#dfe0df");
145 |
146 | translate(width / 2, height / 2);
147 |
148 | const offset = (cos(timeValue * 2) + 1) / 2.0;
149 | const offsetNext = (cos(timeValue * 2 + QUARTER_PI) + 1) / 2.0;
150 | const rotateFactor = Math.floor(timeValue) + easeInOutQuint(timeValue % 1);
151 |
152 | scale(1 + offset * 0.2);
153 |
154 | rotate(rotateFactor * QUARTER_PI);
155 |
156 | infiniteLoop(
157 | 0,
158 | 0,
159 | 50 + offsetNext * 10,
160 | easeInOutQuad(offsetNext) * 80,
161 | easeInOutQuint(offset)
162 | );
163 |
164 | rotate(HALF_PI);
165 | infiniteLoop(
166 | 0,
167 | 0,
168 | 50 + offset * 10,
169 | easeInOutQuad(offset) * offset * 60,
170 | easeInOutQuint(offset)
171 | );
172 |
173 | timeValue += 0.01;
174 | }
175 |
--------------------------------------------------------------------------------
/2023/day_1/day_1.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // From https://easings.net/
5 | float easeInOutQuint(float x) {
6 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
7 | }
8 |
9 | float easeInOutQuad(float x) {
10 | return x < 0.5 ? 2 * x * x : 1 - pow(-2 * x + 2, 2) / 2;
11 | }
12 |
13 | void dashedCircle(float x, float y, float diam, float step, float space) {
14 | pushMatrix();
15 | translate(x, y);
16 |
17 | int segments = int((TWO_PI / (step + space)));
18 | noFill();
19 |
20 | for (int i = 0; i < segments; i++) {
21 | float angle = i * (step + space);
22 | arc(0, 0, diam, diam, angle, angle + step);
23 | }
24 |
25 | pushStyle();
26 | fill(146, 188, 215, 40);
27 | noStroke();
28 | circle(0, 0, diam);
29 | popStyle();
30 |
31 | popMatrix();
32 | }
33 |
34 | void dashedLine(float x1, float y1, float x2, float y2, float step, float space) {
35 | float dist = dist(x1, y1, x2, y2);
36 | int segments = int(dist / (step + space));
37 | float segmentSize = dist / segments;
38 | PVector origin = new PVector(x1, y1);
39 | PVector direction = PVector.sub(new PVector(x2, y2), origin).normalize();
40 |
41 | for (int i = 0; i < segments; i++) {
42 | PVector next = PVector.add(origin, PVector.mult(direction, segmentSize - space));
43 | line(origin.x, origin.y, next.x, next.y);
44 | origin = PVector.add(next, PVector.mult(direction, space));
45 | }
46 | }
47 |
48 | void bezierWithHandles(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
49 | noFill();
50 | stroke(#2f4858);
51 | strokeWeight(2);
52 | bezier(x1, y1, x2, y2, x3, y3, x4, y4);
53 |
54 | strokeWeight(2);
55 | stroke(#df8b4a);
56 | dashedLine(x1, y1, x2, y2, 8, 4);
57 | dashedLine(x3, y3, x4, y4, 8, 4);
58 |
59 | strokeWeight(10);
60 | stroke(#4a5577);
61 | point(x1, y1);
62 | point(x4, y4);
63 |
64 | strokeWeight(4);
65 | stroke(#b15f83);
66 | fill(#dfe0df);
67 | circle(x2, y2, 6);
68 | circle(x3, y3, 6);
69 | }
70 |
71 | void infiniteLoop(float x, float y, float radius, float space, float roundFactor) {
72 | pushMatrix();
73 | translate(x, y);
74 |
75 | float cy = space / 2 + radius;
76 | float cyDist = cy * 2 * roundFactor;
77 | float bxOff = 0;
78 |
79 | // Circle dashed contour
80 | noFill();
81 | stroke(#9eb0a2);
82 | strokeWeight(1);
83 | dashedCircle(0, -cy, radius * 2, PI / 30, PI / 60);
84 | dashedCircle(0, cy, radius * 2, PI / 30, PI / 60);
85 |
86 | // Middle
87 | bezierWithHandles(
88 | bxOff - radius,
89 | cy,
90 | bxOff - radius,
91 | cy - cyDist,
92 | radius,
93 | -cy + cyDist,
94 | radius,
95 | -cy
96 | );
97 |
98 | bezierWithHandles(
99 | bxOff + radius,
100 | cy,
101 | bxOff + radius,
102 | cy - cyDist,
103 | -radius,
104 | -cy + cyDist,
105 | -radius,
106 | -cy
107 | );
108 |
109 | float loopRadius = radius * roundFactor;
110 |
111 | // Bottom loop
112 | bezierWithHandles(
113 | bxOff + radius,
114 | cy,
115 | bxOff + radius,
116 | cy + loopRadius,
117 | bxOff + loopRadius,
118 | cy + radius,
119 | bxOff,
120 | cy + radius
121 | );
122 |
123 | bezierWithHandles(
124 | bxOff - radius,
125 | cy,
126 | bxOff - radius,
127 | cy + loopRadius,
128 | bxOff - loopRadius,
129 | cy + radius,
130 | bxOff,
131 | cy + radius
132 | );
133 |
134 | // Top loop
135 | bezierWithHandles(
136 | radius,
137 | -cy,
138 | radius,
139 | -cy - loopRadius,
140 | loopRadius,
141 | -cy - radius,
142 | 0,
143 | -cy - radius
144 | );
145 |
146 | bezierWithHandles(
147 | -radius,
148 | -cy,
149 | -radius,
150 | -cy - loopRadius,
151 | -loopRadius,
152 | -cy - radius,
153 | 0,
154 | -cy - radius
155 | );
156 |
157 | // Circle center points
158 | strokeWeight(5);
159 | stroke(#4a5577);
160 | point(0, -cy);
161 | point(0, cy);
162 |
163 | popMatrix();
164 | }
165 |
166 | float timeValue = 0;
167 |
168 | void setup() {
169 | size(400, 400);
170 | }
171 |
172 | void draw() {
173 | background(#dfe0df);
174 |
175 | translate(width / 2, height / 2);
176 |
177 | float offset = (cos(timeValue * 2) + 1) / 2.0;
178 | float offsetNext = (cos(timeValue * 2 + QUARTER_PI) + 1) / 2.0;
179 | float rotateFactor = floor(timeValue) + easeInOutQuint(timeValue % 1);
180 |
181 | scale(1 + offset * 0.2);
182 | rotate(rotateFactor * QUARTER_PI);
183 |
184 | infiniteLoop(0, 0, 50 + offsetNext * 10, easeInOutQuad(offsetNext) * 80, easeInOutQuint(offset));
185 |
186 | rotate(HALF_PI);
187 | infiniteLoop(0, 0, 50 + offset * 10, easeInOutQuad(offset) * offset * 60, easeInOutQuint(offset));
188 |
189 | timeValue += 0.01;
190 | }
191 |
--------------------------------------------------------------------------------
/2023/day_1/genuary_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_1/genuary_1.gif
--------------------------------------------------------------------------------
/2023/day_10/day_10.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | // Dodeka music notation from: https://www.dodekamusic.com
5 |
6 | import processing.sound.*;
7 | import java.util.List;
8 |
9 | final int NNOTES = 12;
10 |
11 | final float NOTE_WIDTH = 35;
12 | final float NOTE_HEIGHT = NOTE_WIDTH / 8;
13 |
14 | // Note durations
15 | final float CROTCHET = 1 / 4f;
16 | final float QUAVER = CROTCHET / 2;
17 | final float SEMI_QUAVER = QUAVER / 2;
18 | final float DEMI_SEMI_QUAVER = SEMI_QUAVER / 2;
19 |
20 | final float CROTCHET_WIDTH = CROTCHET * NOTE_WIDTH;
21 |
22 | class Note {
23 | float time;
24 | int pitch;
25 | float duration;
26 |
27 | Note(float time, int pitch, float duration) {
28 | this.time = time;
29 | this.pitch = pitch;
30 | this.duration = duration;
31 | }
32 |
33 | float width() {
34 | return NOTE_WIDTH * duration;
35 | }
36 |
37 | float start() {
38 | return time * NOTE_WIDTH;
39 | }
40 |
41 | float end() {
42 | return start() + width();
43 | }
44 | }
45 |
46 | class Staff {
47 | float gap = 5;
48 | float barHeight = NOTE_HEIGHT * 2;
49 | float baseFrequency;
50 |
51 | float x, y;
52 |
53 | TriOsc sineOsc;
54 | List notes = new ArrayList();
55 |
56 | Staff(PApplet p, float x, float y, float baseFrequency) {
57 | sineOsc = new TriOsc(p);
58 | sineOsc.play();
59 | this.x = x;
60 | this.y = y;
61 | this.baseFrequency = baseFrequency;
62 | }
63 |
64 | Note lasNote() {
65 | if (notes.size() == 0) return null;
66 | Note lastNote = notes.get(0);
67 |
68 | for (Note note : notes) {
69 | if (note.time > lastNote.time) lastNote = note;
70 | }
71 |
72 | return lastNote;
73 | }
74 |
75 | void display(float time) {
76 | Note lastNote = lasNote();
77 | Note currentPlayingNote = null;
78 |
79 | float lastNoteEnd = lastNote.time + lastNote.duration;
80 | float w = gap * 2 + lastNote.end();
81 | float timelineX = x + time * w;
82 |
83 | noFill();
84 | stroke(0);
85 | for (int i = 0; i < 3; i++) {
86 | rect(x, y + i * barHeight, w, barHeight);
87 | }
88 |
89 | noStroke();
90 | for (int n = 0; n < notes.size(); n++) {
91 | Note note = notes.get(n);
92 |
93 | float notePos = note.time / lastNoteEnd;
94 | color fill = lerpColor(#845ec2, #faccff, note.pitch / (float) NNOTES);
95 |
96 | if (time >= notePos && time <= notePos + note.duration / lastNoteEnd) {
97 | fill = color(0, 255, 0);
98 | currentPlayingNote = note;
99 | }
100 |
101 | float nx = x + gap + note.start();
102 | float ny = y - NOTE_HEIGHT / 2.0 + (NNOTES - note.pitch) * NOTE_HEIGHT / 2.0;
103 |
104 | // We display a half note if greater than one
105 | if (note.duration > CROTCHET) {
106 | fill(255);
107 | rect(nx, ny, note.width(), NOTE_HEIGHT);
108 |
109 | int nDivs = round(note.duration / CROTCHET);
110 | int arrowSize = 5;
111 | int arrowGap = 3;
112 |
113 | fill(fill);
114 | for (int i = 0; i < nDivs; i++) {
115 | boolean first = i == 0;
116 | boolean last = i == nDivs - 1;
117 |
118 | float startX = nx + i * CROTCHET_WIDTH;
119 | float realStartX = first ? startX : startX - arrowSize + arrowGap;
120 | float endX = startX + CROTCHET_WIDTH;
121 | float realEndX = last ? endX : endX - arrowSize;
122 |
123 | beginShape();
124 | vertex(realStartX, ny);
125 | vertex(realEndX, ny);
126 | if (!last) vertex(endX, ny + NOTE_HEIGHT / 2);
127 | vertex(realEndX, ny + NOTE_HEIGHT);
128 | vertex(realStartX, ny + NOTE_HEIGHT);
129 | if (!first) vertex(startX + arrowGap, ny + NOTE_HEIGHT / 2);
130 | endShape();
131 | }
132 | } else {
133 | fill(fill);
134 | rect(nx, ny, note.width(), NOTE_HEIGHT);
135 | }
136 | }
137 |
138 | // Display timeline
139 | stroke(255, 0, 0);
140 | line(timelineX, y, timelineX, y + barHeight * 3);
141 |
142 | // Set frequency
143 | sineOsc.freq(currentPlayingNote != null ? map(currentPlayingNote.pitch, 0, NNOTES, baseFrequency, baseFrequency + 1000) : 0);
144 | }
145 | }
146 |
147 | float animValue = 0;
148 | Staff staff;
149 | List staffs = new ArrayList();
150 |
151 | void setup() {
152 | size(400, 400);
153 |
154 | noiseDetail(8, 0.2);
155 |
156 | int nStaffs = 10;
157 | float gap = 12;
158 | float staffWidth = width - gap * 2;
159 | float maxDuration = staffWidth / NOTE_WIDTH;
160 |
161 | for (int j = 0; j < nStaffs; j++) {
162 | Staff staff = new Staff(this, gap, gap + j * (3 * NOTE_HEIGHT + gap * 2), j * 100);
163 |
164 | float totalDuration = 0;
165 | for (int i = 0; i < NNOTES * 2; i++) {
166 | float fac = (i / (float)NNOTES * 2) * 5;
167 | float offset = j * 3;
168 |
169 | float duration = pow(2, noise(fac + offset) * j * 0.55 - 1) * CROTCHET;
170 | float pitch = noise(fac + offset) * NNOTES;
171 | staff.notes.add(new Note(min(totalDuration, maxDuration), int(pitch), duration));
172 | totalDuration += duration + random(0.05, 0.1);
173 |
174 | if (totalDuration > maxDuration) break;
175 | }
176 |
177 | staffs.add(staff);
178 | }
179 | }
180 |
181 | void draw() {
182 | background(#fbeaff);
183 |
184 | for (Staff staff : staffs) staff.display(animValue % 1);
185 |
186 | animValue += 0.005;
187 | }
188 |
--------------------------------------------------------------------------------
/2023/day_10/genuary_10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_10/genuary_10.gif
--------------------------------------------------------------------------------
/2023/day_11/day_11.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | float easeOutElastic(float x) {
7 | float c4 = (2 * PI) / 3;
8 |
9 | return x == 0
10 | ? 0
11 | : x == 1
12 | ? 1
13 | : pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1;
14 | }
15 |
16 | color lighten(color c, float l) {
17 | float r = red(c);
18 | float g = green(c);
19 | float b = blue(c);
20 | return color(r * l, g * l, b * l);
21 | }
22 |
23 | PImage fillPImage(int w, int h, color from, color to) {
24 | noiseSeed((long) random(1000));
25 | noiseDetail(4, 0.2);
26 |
27 | PImage pg = createImage(w, h, RGB);
28 | pg.loadPixels();
29 | for (int x = 0; x < pg.width; x++) {
30 | for (int y = 0; y < pg.height; y++) {
31 | float n = noise(x / 2, y / 2);
32 | pg.pixels[x + y * pg.width] = lerpColor(from, to, 1.0 - n);
33 | }
34 | }
35 | pg.updatePixels();
36 | return pg;
37 | }
38 |
39 | class Shape {
40 | PImage img;
41 | float x, y, w, h, rotation;
42 | color from, to;
43 |
44 | Shape(color c) {
45 | int gap = 50;
46 | x = random(gap, width - gap);
47 | y = random(gap, height - gap);
48 | w = random(100);
49 | h = random(100);
50 | rotation = random(TWO_PI);
51 | from = c;
52 | to = lighten(c, random(1.1, 1.5));
53 |
54 | img = fillPImage((int)w, (int)h, from, to);
55 | }
56 |
57 | void display() {
58 | imageMode(CENTER);
59 | pushMatrix();
60 | translate(x, y);
61 | rotate(rotation);
62 | image(img, 0, 0);
63 | popMatrix();
64 | }
65 | }
66 |
67 | PImage background;
68 | int animTime = 0;
69 | int animDuration = 2000;
70 | List fromShapes, toShapes;
71 |
72 | final color[] COLORS = {#c29f03, #332836, #a82217, #083277, #196f25};
73 |
74 | List generateShapes() {
75 | List shapes = new ArrayList();
76 |
77 | for (int i = 0; i < 20; i++) {
78 | color rColor = COLORS[int(random(COLORS.length))];
79 | shapes.add(new Shape(rColor));
80 | }
81 |
82 | return shapes;
83 | }
84 |
85 | void setup() {
86 | size(400, 400);
87 |
88 | background = fillPImage(width, height, #f4f3ee, #d6cec9);
89 | fromShapes = new ArrayList();
90 | toShapes = new ArrayList();
91 |
92 | fromShapes = generateShapes();
93 | toShapes = generateShapes();
94 |
95 | animTime = millis();
96 | }
97 |
98 | void draw() {
99 | imageMode(CORNER);
100 | image(background, 0, 0);
101 |
102 | int timeDiff = millis() - animTime;
103 |
104 | if (timeDiff >= animDuration) {
105 | animTime = millis();
106 | fromShapes = toShapes;
107 | toShapes = generateShapes();
108 | }
109 |
110 | float animFac = easeOutElastic ((float) timeDiff / animDuration);
111 |
112 | for (int i = 0; i < toShapes.size(); i++) {
113 | Shape s1 = fromShapes.get(i);
114 | Shape s2 = toShapes.get(i);
115 |
116 | float x = lerp(s1.x, s2.x, animFac);
117 | float y = lerp(s1.y, s2.y, animFac);
118 | float rotation = lerp(s1.rotation, s2.rotation, animFac);
119 | float w = lerp(s1.w, s2.w, animFac);
120 | float h = lerp(s1.h, s2.h, animFac);
121 |
122 | pushMatrix();
123 | translate(x, y);
124 | rotate(rotation);
125 | imageMode(CENTER);
126 | image(s1.img, 0, 0, w, h);
127 | popMatrix();
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/2023/day_11/genuary_11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_11/genuary_11.gif
--------------------------------------------------------------------------------
/2023/day_12/day_12.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | float tWidth = 10;
5 | float tHeight = tWidth * 3;
6 | float tLength = tHeight;
7 | float tTotalLength = 2 * tWidth + tLength;
8 |
9 | float easeInOutQuint(float x) {
10 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
11 | }
12 |
13 | void tShape(float x, float y, float rotate, float scale) {
14 | pushMatrix();
15 | translate(x, y);
16 | rotate(rotate);
17 | scale(scale);
18 |
19 | noStroke();
20 |
21 | beginShape();
22 | vertex(0, 0);
23 | vertex(tWidth, 0);
24 | vertex(tWidth, tWidth);
25 | vertex(tWidth + tLength, tWidth);
26 | vertex(tWidth + tLength, 0);
27 | vertex(tWidth + tLength + tWidth, 0);
28 | vertex(tWidth + tLength + tWidth, tHeight);
29 | vertex(tWidth + tLength, tHeight);
30 | vertex(tWidth + tLength, tHeight - tWidth);
31 | vertex(tWidth, tHeight - tWidth);
32 | vertex(tWidth, tHeight);
33 | vertex(0, tHeight);
34 | endShape(CLOSE);
35 |
36 | popMatrix();
37 | }
38 |
39 | float normalizeOsc(float v) {
40 | return easeInOutQuint((v + 1) / 2) * 2 -1;
41 | }
42 |
43 | void horizontalGrid(float x, float y, color c, float anim) {
44 | pushMatrix();
45 | translate(x, y);
46 |
47 | float ty = 0;
48 |
49 | fill(c);
50 | while (ty < height) {
51 | float tx = 0;
52 | while (tx < width) {
53 | float txNorm = norm(tx, 0, height);
54 | tShape(tx, ty, 0, normalizeOsc(sin(txNorm + anim)));
55 | tx += tTotalLength + tWidth;
56 | }
57 | ty += 2 * tHeight;
58 | }
59 |
60 | popMatrix();
61 | }
62 |
63 | void verticalGrid(float x, float y, color c, float anim) {
64 | pushMatrix();
65 | translate(x, y);
66 |
67 | float tx = tHeight;
68 |
69 | fill(c);
70 | while (tx < width) {
71 | float ty = 0;
72 | while (ty < height) {
73 | float tyNorm = norm(ty, 0, height);
74 | tShape(tx, ty, HALF_PI, normalizeOsc(cos(tyNorm + anim)));
75 | ty += tTotalLength + tWidth;
76 | }
77 | tx += 2 * tHeight;
78 | }
79 |
80 | popMatrix();
81 | }
82 |
83 | float animValue = 0;
84 |
85 | void setup() {
86 | size(400, 400);
87 | }
88 |
89 | void draw() {
90 | background(255);
91 |
92 | horizontalGrid(0, 0, #e92525, animValue + PI);
93 | horizontalGrid(tLength, tHeight, #ff8ba8, animValue + PI);
94 |
95 | verticalGrid(tWidth, tWidth * 2, #ff5968, animValue - PI);
96 | verticalGrid(tWidth + tLength, -tWidth, #ffbce3, animValue - PI);
97 |
98 | stroke(255);
99 | strokeWeight(tWidth * 6);
100 | noFill();
101 | rect(0, 0, width, height);
102 |
103 | animValue += 0.05;
104 | }
105 |
--------------------------------------------------------------------------------
/2023/day_12/genuary_12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_12/genuary_12.gif
--------------------------------------------------------------------------------
/2023/day_2/day_2.js:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | class Loupe {
5 | constructor(x, y, targetX, targetY) {
6 | this.origin = new p5.Vector(x, y);
7 | this.target = new p5.Vector(targetX, targetY);
8 | }
9 |
10 | display() {
11 | stroke(0);
12 | strokeWeight(5);
13 | line(this.origin.x, this.origin.y, this.target.x, this.target.y);
14 |
15 | fill(255);
16 | stroke(0);
17 | strokeWeight(10);
18 |
19 | circle(this.target.x, this.target.y, 60);
20 | }
21 | }
22 |
23 | function randomBorderCoordinate() {
24 | const side = Math.floor(random(4));
25 | switch (side) {
26 | case 0:
27 | return new p5.Vector(random(width), -10);
28 | case 1:
29 | return new p5.Vector(width + 10, random(height));
30 | case 2:
31 | return new p5.Vector(random(width) + 10, height);
32 | case 3:
33 | return new p5.Vector(-10, random(height));
34 | }
35 | }
36 |
37 | const loupes = [];
38 |
39 | function setup() {
40 | createCanvas(400, 400);
41 |
42 | for (let i = 0; i < 10; i++) {
43 | const borderPoint = randomBorderCoordinate();
44 | const randomX = random(width);
45 | const randomY = random(height);
46 |
47 | loupes.push(new Loupe(borderPoint.x, borderPoint.y, randomX, randomY));
48 | }
49 | }
50 |
51 | function draw() {
52 | background(255);
53 |
54 | for (const loupe of loupes) {
55 | loupe.display();
56 | }
57 |
58 | noLoop();
59 | }
60 |
--------------------------------------------------------------------------------
/2023/day_2/genuary_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_2/genuary_2.png
--------------------------------------------------------------------------------
/2023/day_3/day_3.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | float time = 0;
5 |
6 | float easeOutQuad(float x) {
7 | return 1 - (1 - x) * (1 - x);
8 | }
9 |
10 | class Section {
11 | color col;
12 | PImage section;
13 | PVector center, dir;
14 | float dist;
15 |
16 | Section(color col, PImage section, int x, int y) {
17 | this.col = col;
18 | this.section = section;
19 | this.center = new PVector(x, y);
20 | this.dir = PVector.fromAngle(hue(col));
21 | this.dist = random(5, 15);
22 | }
23 |
24 | void display() {
25 | pushMatrix();
26 | float offset = random(5);
27 | float offsetX = offset + easeOutQuad(sin(time)) * 10;
28 | float offsetY = offset + easeOutQuad(cos(time)) * 5;
29 | translate(center.x + random(-offset, offsetX), center.y + random(-offset, offsetY));
30 | image(section, 0, 0);
31 | popMatrix();
32 | }
33 | }
34 |
35 | void sampleSquares() {
36 | for (int i = 0; i < 100; i++) {
37 | int size = int(random(5, 50));
38 | int x = int(random(width - size));
39 | int y = int(random(height - size));
40 |
41 | PImage section = get(x, y, size, size);
42 | new Section(pixels[x + y * width], section, x, y).display();
43 | }
44 | }
45 |
46 | void initialGradient() {
47 | loadPixels();
48 |
49 | for (int x = 0; x < width; x++) {
50 | float xnorm = x / (float) width;
51 | for (int y = 0; y < height; y++) {
52 | float ynorm = y / (float) height;
53 | int loc = x + y * width;
54 |
55 | pixels[loc] = color((1 - xnorm) * 200, ynorm * 255, 255);
56 | }
57 | }
58 |
59 | updatePixels();
60 | }
61 |
62 | void setup() {
63 | size(400, 400);
64 | frameRate(30);
65 |
66 | initialGradient();
67 | }
68 |
69 | void draw() {
70 | sampleSquares();
71 | time += 0.05;
72 | }
73 |
--------------------------------------------------------------------------------
/2023/day_3/genuary_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_3/genuary_3.gif
--------------------------------------------------------------------------------
/2023/day_4/day_4.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | float easeOutExpo(float x) {
5 | return x == 1 ? 1 : 1 - pow(2, -10 * x);
6 | }
7 |
8 | float easeInOutQuint(float x) {
9 | return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
10 | }
11 |
12 | void lineBorder(float x1, float y1, float x2, float y2, float sw, float sp) {
13 | strokeCap(SQUARE);
14 | stroke(255);
15 | strokeWeight(sp);
16 | line(x1, y1, x2, y2);
17 | stroke(0);
18 | strokeWeight(sw);
19 | line(x1, y1, x2, y2);
20 | }
21 |
22 | void lineBorderFromPointAngle(float x, float y, float angle, float sw, float sp, float size, float anim) {
23 | pushMatrix();
24 | translate(x, y);
25 | rotate(angle);
26 |
27 | lineBorder(-size / 2, 0, -size / 2 + size * anim, 0, sw, sp);
28 |
29 | popMatrix();
30 | }
31 |
32 | float movingFactor(float value) {
33 | return floor(value) + easeInOutQuint(value % 1);
34 | }
35 |
36 | float animValue = 0;
37 |
38 | void setup() {
39 | size(400, 400);
40 | }
41 |
42 | void draw() {
43 | background(255);
44 |
45 | float rotateFactor = movingFactor(animValue);
46 |
47 | int nPoints = 15;
48 |
49 | for (int i = 0; i < nPoints; i++) {
50 | float fac = (i / (float) nPoints);
51 | float x = fac * width;
52 | lineBorderFromPointAngle(x, x, -QUARTER_PI * rotateFactor, 8, 16, width * 2, 1);
53 | lineBorderFromPointAngle(width - x, x, QUARTER_PI * rotateFactor, 8, 16, width * 2, 1);
54 |
55 |
56 | lineBorderFromPointAngle(x, height - x, -QUARTER_PI / 2 * rotateFactor, 8, 16, width * 2, 1);
57 | lineBorderFromPointAngle(width - x, height - x, QUARTER_PI / 2 * rotateFactor, 8, 16, width * 2, 1);
58 | }
59 |
60 | // White border
61 | noFill();
62 | strokeWeight(50);
63 | stroke(255);
64 | rect(0, 0, width, height);
65 |
66 | animValue += 0.02;
67 | }
68 |
--------------------------------------------------------------------------------
/2023/day_4/genuary_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_4/genuary_4.gif
--------------------------------------------------------------------------------
/2023/day_5/day_5.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | boolean displayStack = false;
5 |
6 | void displayStackTrace() {
7 | fill(0, 255, 0);
8 |
9 | int i = 0;
10 | for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
11 | text(ste.toString(), 10, 20 + i * 15);
12 | i++;
13 | }
14 |
15 | displayStack = true;
16 | }
17 |
18 | void circleRecursive(float x, float y, float size, int depth, int maxDepth) {
19 | if (depth >= maxDepth) {
20 | if (!displayStack) displayStackTrace();
21 | return;
22 | }
23 |
24 | pushMatrix();
25 | translate(x, y);
26 |
27 | noFill();
28 | stroke(255);
29 |
30 | circle(0, 0, size);
31 | popMatrix();
32 |
33 | for (int i = 0; i < 10; i++) {
34 | float rsize = random(size / 12, size /1.5);
35 | float angle = random(TWO_PI);
36 | float radius = random((size - rsize) / 2.0);
37 | float nextX = cos(angle) * radius;
38 | float nextY = sin(angle) * radius;
39 | if (random(1) < 0.5 - depth * 0.05) circleRecursive(x + nextX, y + nextY, rsize, depth + 1, maxDepth);
40 | }
41 | }
42 |
43 | float animValue = 0;
44 |
45 | void setup() {
46 | size(400, 400);
47 | }
48 |
49 | void draw() {
50 | background(0);
51 |
52 | float cosValue = (cos(animValue) + 1) / 2.0;
53 | int depth = floor(cosValue * 10);
54 |
55 | circleRecursive(width / 2, height / 2, 300, 0, depth);
56 |
57 | animValue += 0.1;
58 | displayStack = false;
59 | }
60 |
--------------------------------------------------------------------------------
/2023/day_5/genuary_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_5/genuary_5.gif
--------------------------------------------------------------------------------
/2023/day_6/day_6.js:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // Credit: Vahram Muratyan
3 | // This code is licensed under MIT license (see LICENSE for details)
4 |
5 | function computeGradient(fac, middle, size) {
6 | const half = size / 2;
7 | if (fac <= middle - half) return 0;
8 | else if (fac >= middle + half) return 1;
9 | return (fac - (middle - half)) / size;
10 | }
11 |
12 | function easeInOutSine(x) {
13 | return -(Math.cos(Math.PI * x) - 1) / 2;
14 | }
15 |
16 | function windowMask(w, h) {
17 | const mask = createGraphics(width, height);
18 |
19 | mask.fill(255);
20 | mask.stroke(255);
21 | mask.strokeWeight(10);
22 | mask.rectMode(CENTER);
23 |
24 | mask.rect(width / 2, height / 2, w, h, 40);
25 |
26 | return mask;
27 | }
28 |
29 | let blue;
30 | let pink;
31 | let red;
32 |
33 | let rh, rw;
34 | let rectMask;
35 | let drawing;
36 | let animValue = 0;
37 |
38 | function lerpValue(offset, speed, min, max) {
39 | return min + (max - min) * easeInOutSine(animValue * speed + offset);
40 | }
41 |
42 | function setup() {
43 | createCanvas(400, 400);
44 |
45 | blue = color("#2e3e8c");
46 | pink = color("#fbc9be");
47 | red = color("#fd312b");
48 |
49 | rh = width * 0.8;
50 | rw = rh / 1.5;
51 | }
52 |
53 | function draw() {
54 | drawing = createGraphics(width, height);
55 | rectMask = windowMask(rw, rh);
56 |
57 | // Lines
58 | drawing.push();
59 | drawing.translate((width - rw) / 2, 0);
60 | const startLineY = (height - rh) / 2;
61 | const gradientPos = lerpValue(QUARTER_PI, 0.5, 0.2, 0.5);
62 | const gradientSize = lerpValue(0, 1, 0.15, 0.25);
63 |
64 | for (let i = 0; i <= rh; i++) {
65 | const y = startLineY + i;
66 | const gradientFac = computeGradient(i / rh, gradientPos, gradientSize);
67 | const lineCol = lerpColor(blue, pink, gradientFac);
68 | drawing.stroke(lineCol);
69 | drawing.line(0, y, rw, y);
70 | }
71 | drawing.pop();
72 |
73 | // Fuji
74 | const fujiPos = lerpValue(PI, 0.5, 0.7, 0.8);
75 | const fujiHeightFac = 0.4;
76 | const fujiTopWidthFac = 0.1;
77 | const fujiY = height / 2 + (fujiPos - 0.5) * rh;
78 | const fujiTopWidth = fujiTopWidthFac * rw;
79 | const fujiHeight = fujiHeightFac * rw;
80 |
81 | let fuji = createGraphics(width, height);
82 |
83 | const anchorDistance = (width * 1.5) / 2;
84 | const topDist = fujiTopWidth / 2;
85 | const topCurvatureDist = 0.2 * fujiHeight;
86 | const bottomCurvatureDist = 0.5 * rw;
87 |
88 | fuji.fill(blue);
89 | fuji.noStroke();
90 |
91 | fuji.push();
92 | fuji.translate(width / 2, fujiY);
93 | fuji.beginShape();
94 |
95 | fuji.vertex(-anchorDistance, 0);
96 | fuji.bezierVertex(
97 | -bottomCurvatureDist,
98 | 0,
99 | -topDist,
100 | -fujiHeight + topCurvatureDist,
101 | -topDist,
102 | -fujiHeight
103 | );
104 | fuji.bezierVertex(
105 | -topDist,
106 | -fujiHeight,
107 | -topDist,
108 | -fujiHeight,
109 | topDist,
110 | -fujiHeight
111 | );
112 | fuji.bezierVertex(
113 | topDist,
114 | -fujiHeight + topCurvatureDist,
115 | bottomCurvatureDist,
116 | 0,
117 | anchorDistance,
118 | 0
119 | );
120 |
121 | fuji.endShape();
122 | fuji.pop();
123 |
124 | // Bottom fuji blue
125 | fuji.rect((width - rw) / 2, fujiY - 10, rw, rh);
126 |
127 | // Top snow
128 | let fujiSnow = createGraphics(width, height);
129 | const fujiSnowRatio = lerpValue(PI, 0.5, 0.7, 0.9);
130 | fujiSnow.fill(255);
131 | fujiSnow.noStroke();
132 | fujiSnow.rectMode(CENTER);
133 | fujiSnow.rect(width / 2, fujiY - fujiHeight, rw, fujiSnowRatio * fujiHeight);
134 | fujiSnow.drawingContext.globalCompositeOperation = "destination-in";
135 | fujiSnow.image(fuji, 0, 0);
136 |
137 | // Apply snow
138 | drawing.image(fuji, 0, 0);
139 | drawing.image(fujiSnow, 0, 0);
140 |
141 | // Mask
142 | rectMask.drawingContext.globalCompositeOperation = "source-in";
143 | rectMask.image(drawing, 0, 0);
144 |
145 | // Background
146 | background(red);
147 | image(rectMask, 0, 0);
148 |
149 | // Border
150 | noFill();
151 | stroke(255);
152 | strokeWeight(10);
153 | rectMode(CENTER);
154 | rect(width / 2, height / 2, rw, rh, 40);
155 |
156 | // red circle
157 | const angle = animValue;
158 | fill(red);
159 | noStroke();
160 | circle(
161 | width / 2 + rw / 4 + cos(angle) * 20,
162 | height / 2 - rh / 3 + sin(angle) * 20,
163 | 15
164 | );
165 |
166 | // Clean
167 | fuji.remove();
168 | fujiSnow.remove();
169 | drawing.remove();
170 | rectMask.remove();
171 | fuji = null;
172 | fujiSnow = null;
173 | drawing = null;
174 | rectMask = null;
175 |
176 | animValue += 0.05;
177 | }
178 |
--------------------------------------------------------------------------------
/2023/day_6/genuary_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_6/genuary_6.gif
--------------------------------------------------------------------------------
/2023/day_6/genuary_6.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // Credit: Vahram Muratyan
3 | // This code is licensed under MIT license (see LICENSE for details)
4 |
5 | float computeGradient(float fac, float middle, float size) {
6 | float half = size / 2;
7 | if (fac <= middle - half) return 0;
8 | else if (fac >= middle + half) return 1;
9 | return (fac - (middle - half)) / size;
10 | }
11 |
12 | float easeInOutSine(float x) {
13 | return -(cos(PI * x) - 1) / 2;
14 | }
15 |
16 | float easeInOutCirc(float x) {
17 | return x < 0.5
18 | ? (1 - sqrt(1 - pow(2 * x, 2))) / 2
19 | : (sqrt(1 - pow(-2 * x + 2, 2)) + 1) / 2;
20 | }
21 |
22 | PGraphics windowMask(int w, int h) {
23 | PGraphics mask = createGraphics(width, height);
24 |
25 | mask.beginDraw();
26 | mask.background(0);
27 | mask.fill(255);
28 | mask.stroke(255);
29 | mask.strokeWeight(10);
30 | mask.rectMode(CENTER);
31 |
32 | mask.translate(width / 2.0, height / 2.0);
33 |
34 | mask.rect(0, 0, w, h, 40);
35 | mask.endDraw();
36 |
37 | return mask;
38 | }
39 |
40 | void maskWith(PGraphics origin, PGraphics mask, boolean keepMask) {
41 | origin.loadPixels();
42 | mask.loadPixels();
43 | for (int i = 0; i < origin.width * origin.height; i++) {
44 | if (mask.pixels[i] == color(0)) {
45 | origin.pixels[i] = color(0, 0);
46 | } else if (keepMask && alpha(origin.pixels[i]) > 50) {
47 | origin.pixels[i] = mask.pixels[i];
48 | }
49 | }
50 | origin.updatePixels();
51 | }
52 |
53 | color blue = #2e3e8c;
54 | color pink = #fbc9be;
55 | color red = #fd312b;
56 |
57 | float rh, rw;
58 | PGraphics rectMask;
59 | PGraphics drawing;
60 | float animValue = 0;
61 |
62 | float lerpValue(float offset, float speed, float min, float max) {
63 | return min + (max - min) * easeInOutSine((cos(animValue * speed + offset) + 1) / 2);
64 | }
65 |
66 | void setup() {
67 | size(400, 400);
68 |
69 | rh = width * 0.8;
70 | rw = rh / 1.5;
71 | }
72 |
73 | void draw() {
74 | drawing = createGraphics(width, height);
75 | rectMask = windowMask((int) rw, (int) rh);
76 |
77 | // Lines
78 | drawing.beginDraw();
79 | drawing.push();
80 | drawing.translate((width - rw) / 2, 0);
81 | float startLineY = (height - rh) / 2;
82 | float gradientPos = lerpValue(QUARTER_PI, 0.5, 0.2, 0.5);
83 | float gradientSize = lerpValue(0, 1, 0.15, 0.25);
84 |
85 | for (int i = 0; i <= rh; i++) {
86 | float y = startLineY + i;
87 | float gradientFac = computeGradient(i / rh, gradientPos, gradientSize);
88 | color lineCol = lerpColor(blue, pink, gradientFac);
89 | drawing.stroke(lineCol);
90 | drawing.line(0, y, rw, y);
91 | }
92 | drawing.pop();
93 |
94 | // Fuji
95 | float fujiPos = lerpValue(PI, 0.5, 0.7, 0.8);
96 | float fujiHeightFac = 0.4;
97 | float fujiTopWidthFac = 0.1;
98 | float fujiY = height / 2 + (fujiPos - 0.5) * rh;
99 | float fujiTopWidth = fujiTopWidthFac * rw;
100 | float fujiHeight = fujiHeightFac * rw;
101 |
102 | PGraphics fuji = createGraphics(width, height);
103 |
104 | float anchorDistance = (width * 1.5) / 2;
105 | float topDist = fujiTopWidth / 2;
106 | float topCurvatureDist = 0.2 * fujiHeight;
107 | float bottomCurvatureDist = 0.5 * rw;
108 |
109 | fuji.beginDraw();
110 | fuji.fill(blue);
111 | fuji.noStroke();
112 |
113 | fuji.push();
114 | fuji.translate(width / 2, fujiY);
115 | fuji.beginShape();
116 |
117 | fuji.vertex(-anchorDistance, 0);
118 | fuji.bezierVertex(
119 | -bottomCurvatureDist,
120 | 0,
121 | -topDist,
122 | -fujiHeight + topCurvatureDist,
123 | -topDist,
124 | -fujiHeight
125 | );
126 | fuji.bezierVertex(
127 | -topDist,
128 | -fujiHeight,
129 | -topDist,
130 | -fujiHeight,
131 | topDist,
132 | -fujiHeight
133 | );
134 | fuji.bezierVertex(
135 | topDist,
136 | -fujiHeight + topCurvatureDist,
137 | bottomCurvatureDist,
138 | 0,
139 | anchorDistance,
140 | 0
141 | );
142 |
143 | fuji.endShape();
144 | fuji.pop();
145 |
146 | // Bottom fuji blue
147 | fuji.rect((width - rw) / 2, fujiY - 10, rw, rh);
148 | fuji.endDraw();
149 |
150 | // Top snow
151 | PGraphics fujiSnow = createGraphics(width, height);
152 | float fujiSnowRatio = lerpValue(PI, 0.5, 0.7, 0.9);
153 |
154 | fujiSnow.beginDraw();
155 | fujiSnow.background(0);
156 | fujiSnow.fill(255);
157 | fujiSnow.noStroke();
158 | fujiSnow.rectMode(CENTER);
159 | fujiSnow.rect(width / 2, fujiY - fujiHeight, rw, fujiSnowRatio * fujiHeight);
160 | // fujiSnow.image(fuji, 0, 0);
161 | fujiSnow.endDraw();
162 |
163 | drawing.image(fuji, 0, 0);
164 |
165 | // Apply snow
166 | maskWith(fuji, fujiSnow, true);
167 | drawing.image(fuji, 0, 0);
168 |
169 | drawing.endDraw();
170 |
171 | // Mask the whole thing
172 | maskWith(drawing, rectMask, false);
173 |
174 | // Background
175 | background(red);
176 | image(drawing, 0, 0);
177 |
178 | // Border
179 | noFill();
180 | stroke(255);
181 | strokeWeight(10);
182 | rectMode(CENTER);
183 | rect(width / 2, height / 2, rw, rh, 40);
184 |
185 | // Red circle
186 | float angle = animValue;
187 | fill(red);
188 | noStroke();
189 | circle(width / 2 + rw / 4 + cos(angle) * 20, height / 2 - rh / 3 + sin(angle * 2) * 20, 15);
190 |
191 | animValue += 0.05;
192 |
193 | if (frameCount % 2 == 0) saveFrame("anim.####.png");
194 | if (animValue > TWO_PI * 2) noLoop();
195 | }
196 |
--------------------------------------------------------------------------------
/2023/day_7/day_7.js:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | function lerpMultiple(a, b, c, fac) {
5 | return lerpColor(lerpColor(a, c, 1 - fac), lerpColor(b, c, 1 - fac), fac);
6 | }
7 |
8 | let animValue = 0;
9 |
10 | function setup() {
11 | createCanvas(400, 400);
12 | noiseSeed(99);
13 | }
14 |
15 | function draw() {
16 | background(220);
17 |
18 | push();
19 | translate(width / 2, -200);
20 | rectMode(CENTER);
21 |
22 | const from = color("#326b7e");
23 | const to = color("#9fc4d4");
24 | const dark = color("#041a1f");
25 | const fromShadow = color("#0E373914");
26 | const toShadow = color("#95C5D133");
27 | const white = color("#D1ECF105");
28 |
29 | const nTriangles = 20;
30 | const space = (Math.sqrt(2) * width) / nTriangles;
31 |
32 | for (let i = 0; i < nTriangles; i++) {
33 | const fac = i / nTriangles;
34 |
35 | const y = (nTriangles - i) * space;
36 |
37 | const offsetX = cos(fac * 7) * 50;
38 | const w = width;
39 | const triH = 200;
40 |
41 | noStroke();
42 |
43 | fill(lerpMultiple(from, to, dark, fac));
44 | triangle(-w, y, offsetX, y + triH, w, y);
45 |
46 | fill(lerpColor(fromShadow, toShadow, fac));
47 | triangle(-w, y, offsetX, y + triH - 5, w, y);
48 |
49 | stroke(white);
50 | strokeWeight(random(10, 50));
51 | line(0, y, random(-w / 2, w / 2), height + triH);
52 | }
53 | pop();
54 |
55 | const border = 30;
56 | const bw = width - 2 * border;
57 | noFill();
58 | stroke("#bcdee5");
59 | strokeWeight(1);
60 | rectMode(CENTER);
61 | square(width / 2, height / 2, bw);
62 |
63 | circle(width / 2, height - border * 2, 20);
64 | circle(width / 2, height - border * 2, 5);
65 |
66 | textAlign(CENTER, CENTER);
67 | textSize(150);
68 | noStroke();
69 | fill("#BCDEE5BF");
70 | textFont("Verdana");
71 | text(".¤.", width / 2, height / 2);
72 |
73 | animValue += 0.05;
74 | noLoop();
75 | }
76 |
--------------------------------------------------------------------------------
/2023/day_7/genuary_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_7/genuary_7.png
--------------------------------------------------------------------------------
/2023/day_8/day_8.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | float easeOutBounce(float x) {
5 | float n1 = 7.5625;
6 | float d1 = 2.75;
7 |
8 | if (x < 1 / d1) {
9 | return n1 * x * x;
10 | } else if (x < 2 / d1) {
11 | return n1 * (x -= 1.5 / d1) * x + 0.75;
12 | } else if (x < 2.5 / d1) {
13 | return n1 * (x -= 2.25 / d1) * x + 0.9375;
14 | } else {
15 | return n1 * (x -= 2.625 / d1) * x + 0.984375;
16 | }
17 | }
18 |
19 | float easeInOutBack(float x) {
20 | float c1 = 1.70158;
21 | float c2 = c1 * 1.525;
22 |
23 | return x < 0.5
24 | ? (pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
25 | : (pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
26 | }
27 |
28 | interface SDF {
29 | float compute(int x, int y);
30 | }
31 |
32 | class ConstantSDF implements SDF {
33 | float value;
34 |
35 | ConstantSDF(float value) {
36 | this.value = value;
37 | }
38 |
39 | float compute(int x, int y) {
40 | return value;
41 | }
42 | }
43 |
44 | class CircleSDF implements SDF {
45 | float cx, cy, diam;
46 |
47 | CircleSDF(float cx, float cy, float diam) {
48 | this.cx = cx;
49 | this.cy = cy;
50 | this.diam = diam;
51 | }
52 |
53 | float compute(int x, int y) {
54 | return dist(x, y, cx, cy) - diam / 2;
55 | }
56 | }
57 |
58 | class SDFSubtract implements SDF {
59 | SDF sdf1, sdf2;
60 |
61 | SDFSubtract(SDF sdf1, SDF sdf2) {
62 | this.sdf1 = sdf1;
63 | this.sdf2 = sdf2;
64 | }
65 |
66 | float compute(int x, int y) {
67 | return max(sdf1.compute(x, y), -sdf2.compute(x, y));
68 | }
69 | }
70 |
71 | class SDFAdd implements SDF {
72 | SDF sdf1, sdf2;
73 |
74 | SDFAdd(SDF sdf1, SDF sdf2) {
75 | this.sdf1 = sdf1;
76 | this.sdf2 = sdf2;
77 | }
78 |
79 | float compute(int x, int y) {
80 | return min(sdf1.compute(x, y), sdf2.compute(x, y));
81 | }
82 | }
83 |
84 | void displaySDF(SDF sdf, float threshold) {
85 | loadPixels();
86 | for (int x = 0; x < width; x++) {
87 | for (int y = 0; y < height; y++) {
88 | float val = easeOutBounce(constrain(sdf.compute(x, y), -threshold, threshold) / threshold);
89 | pixels[x + y * width] = lerpColor(#f69333, #774e7e, val);
90 | }
91 | }
92 | updatePixels();
93 | }
94 |
95 | float animValue = 0;
96 |
97 | float animOffset(float offset) {
98 | return cos(animValue + offset);
99 | }
100 |
101 | void setup() {
102 | size(400, 400);
103 | frameRate(30);
104 | }
105 |
106 | void draw() {
107 | background(255);
108 |
109 | float aCos = easeInOutBack((cos(animValue) + 1) / 2);
110 | float mAcos = 1 - aCos;
111 |
112 | SDF sdf = new SDFAdd(
113 | new SDFSubtract(
114 | new CircleSDF(width / 2, height / 2, 200),
115 | new CircleSDF(width / 2, height / 2, 200)
116 | ),
117 | new SDFSubtract(
118 | new SDFAdd(
119 | new CircleSDF(width / 2 + mAcos * width / 4, height / 2 + 50, 100),
120 | new CircleSDF(width / 2 - mAcos * width / 4, height / 2 - 50, 100)
121 | ),
122 | new CircleSDF(width / 2, height / 2, 100 * aCos)
123 | )
124 | );
125 |
126 | displaySDF(sdf, 10 + aCos * 80);
127 |
128 | animValue += 0.05;
129 | }
130 |
--------------------------------------------------------------------------------
/2023/day_8/genuary_8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_8/genuary_8.gif
--------------------------------------------------------------------------------
/2023/day_9/day_9.pde:
--------------------------------------------------------------------------------
1 | // (c) 2023 Joseph HENRY
2 | // This code is licensed under MIT license (see LICENSE for details)
3 |
4 | import java.util.List;
5 |
6 | float easeInQuint(float x) {
7 | return x * x * x * x * x;
8 | }
9 |
10 | class Branch {
11 | PVector end, direction;
12 | float speed;
13 | float branchingFactor;
14 | float branchingAngle = radians(50);
15 | float growDirectionRotateStrength = radians(1);
16 | int maxBranchingDepth = 3;
17 | int branchingDepth;
18 | boolean branchSide;
19 | int maxSize;
20 | float originalWidth;
21 | long noiseSeed;
22 | int noiseOctaves;
23 | int leaveAnimation = -1;
24 | float flowerSize;
25 | color flowerColor;
26 |
27 | List points;
28 | List subBranches;
29 |
30 | Branch(float x, float y, float angle) {
31 | this(new PVector(x, y), PVector.fromAngle(angle), 1.5, 300, 0.08, 0, 1.5);
32 | }
33 |
34 | Branch(PVector start, PVector direction, float speed, int maxSize, float branchingFactor, int branchingDepth, float originalWidth) {
35 | this.end = start;
36 | this.direction = direction;
37 | this.points = new ArrayList();
38 | this.subBranches = new ArrayList();
39 | this.speed = speed;
40 | this.branchingFactor = branchingFactor;
41 | this.branchingDepth = branchingDepth;
42 | this.branchSide = random(1) < 0.5;
43 | this.maxSize = maxSize;
44 | this.noiseSeed = (long) random(10000);
45 | this.noiseOctaves = branchingDepth;
46 | this.originalWidth = originalWidth;
47 | this.flowerSize = random(2, 12);
48 | this.flowerColor = lerpColor(#b6567a, #f3b5bc, random(1));
49 | }
50 |
51 | boolean canGrow() {
52 | return branchingDepth < maxBranchingDepth;
53 | }
54 |
55 | boolean finished() {
56 | return points.size() >= maxSize;
57 | }
58 |
59 | float directionInfluence(PVector pushDirection, float force) {
60 | float angle = PVector.angleBetween(direction, PVector.sub(pushDirection, end));
61 | if (angle > PI) angle -= TWO_PI;
62 | return angle * force;
63 | }
64 |
65 | void grow() {
66 | // Grow sub branches
67 | for (Branch subBranch : subBranches) {
68 | if (subBranch.canGrow() || !subBranch.finished()) {
69 | subBranch.grow();
70 | }
71 | }
72 |
73 | if (finished()) return;
74 |
75 | // Compute the next branch point
76 | PVector next = PVector.add(end, PVector.mult(direction, speed));
77 | points.add(next);
78 |
79 | // Randomly create a new branch
80 | if (canGrow() && random(1) < branchingFactor) {
81 | int branchSign = branchSide ? -1 : 1;
82 | branchSide = !branchSide;
83 | float branchAngle = branchSign * branchingAngle;
84 | PVector branchDirection = direction.copy().rotate(branchAngle);
85 | int branchSize = int((float) maxSize / random(1.5, 8));
86 | subBranches.add(new Branch(end, branchDirection, speed * random(0.4, 0.7), branchSize, branchingFactor - 0.02, branchingDepth + 1, originalWidth));
87 | } else {
88 | // Otherwise change direction smoothly
89 | noiseSeed(noiseSeed);
90 | noiseDetail(noiseOctaves * 5, 0.1);
91 |
92 | float angleToLight = directionInfluence(new PVector(width / 2, 50), 0.01);
93 |
94 | // Left
95 | float force = 50;
96 | float pushRight = end.x <= force ? 1 - end.x / force : 0;
97 | float angleLeft = directionInfluence(new PVector(1, 0), pushRight * 0.03);
98 |
99 | // Right
100 | float pushLeft = end.x >= width - force ? (width - end.x) / force : 0;
101 | float angleRight = directionInfluence(new PVector(-1, 0), pushLeft * 0.03);
102 |
103 | float rotate = map(noise(animValue), 0, 1, -growDirectionRotateStrength, growDirectionRotateStrength);
104 | direction.rotate(rotate + angleToLight + angleLeft - angleRight + directionInfluence(new PVector(width / 4, 100), 0.005));
105 | }
106 |
107 | // Move the end of the branch
108 | end = next;
109 | }
110 |
111 | void display() {
112 | stroke(lerpColor(#00a168, #0064c1, (float) branchingDepth / maxBranchingDepth));
113 | noFill();
114 |
115 | for (int i = 0; i < points.size() - 1; i++) {
116 | PVector point = points.get(i);
117 | PVector to = points.get(i + 1);
118 | strokeWeight(((maxSize - i) / (float) maxSize) + originalWidth);
119 | line(point.x, point.y, to.x, to.y);
120 | }
121 |
122 | if (points.size() > 0 && finished()) {
123 | if (leaveAnimation < 0) leaveAnimation = millis();
124 |
125 | PVector branchEnd = points.get(points.size() - 1);
126 | noStroke();
127 | fill(flowerColor);
128 | float leafScale = constrain((millis() - leaveAnimation) / 500.0, 0, 1);
129 | circle(branchEnd.x, branchEnd.y, easeInQuint(leafScale) * flowerSize);
130 | }
131 |
132 | for (Branch subBranch : subBranches) subBranch.display();
133 | }
134 |
135 | int countBranches() {
136 | int count = 1;
137 | for (Branch subBranch : subBranches) count += subBranch.countBranches();
138 | return count;
139 | }
140 | }
141 |
142 | float animValue = 0;
143 | List plants = new ArrayList();
144 |
145 | void initialize() {
146 | plants.clear();
147 |
148 | plants.add(new Branch(120, height * 0.95, -HALF_PI));
149 | plants.add(new Branch(width - 120, height * 0.95, -HALF_PI));
150 | }
151 |
152 | void setup() {
153 | size(400, 400);
154 |
155 | initialize();
156 | }
157 |
158 | void draw() {
159 | background(#d8e1cf);
160 |
161 | for (Branch plant : plants) {
162 | plant.display();
163 | for (int i = 0; i < 5; i++) plant.grow();
164 | }
165 |
166 | animValue += 1;
167 | }
168 |
169 | void mousePressed() {
170 | initialize();
171 | }
172 |
--------------------------------------------------------------------------------
/2023/day_9/genuary_9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johhnry/genuary/0d5b88efa485617319a2214f1f6fa60028b18d19/2023/day_9/genuary_9.gif
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Joseph HENRY
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 | # Genuary
2 |
3 | This repository stores the sketches made during the [Genuary](https://genuary.art/) event.
4 |
5 | > GENUARY is an artificially generated month of time where we build code that makes beautiful things.
6 |
7 | Here are the past editions, click on one of them to see the all the animations!
8 |
9 | ## => [2021 Edition](./2021/README.md)
10 |
11 |
12 |
13 | ## => [2023 Edition](./2023/README.md)
14 |
15 |
16 |
17 | ## Links
18 |
19 | - [Genuary home page](https://genuary.art/)
20 |
21 | - [p5js Website](p5js.org/)
22 |
23 | - [p5js Web Editor](https://editor.p5js.org/)
24 |
25 | ## License
26 |
27 | [MIT](https://choosealicense.com/licenses/mit/)
28 |
--------------------------------------------------------------------------------