├── 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/genuary_1.gif)](./day_1/day_1.js)
`// TRIPLE NESTED LOOP`
| [![](./day_2/genuary_2.gif)](./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/genuary_3.gif)](./day_3/day_3.js)
Make something human.
| 4 | | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | 5 | | [![](./day_4/genuary_4.gif)](./day_4/day_4.js)
Small areas of symmetry.
| [![](./day_5/genuary_5.gif)](./day_5/day_5.js)
Do some code golf!
| [![](./day_6/genuary_6.gif)](./day_6/day_6.js)
Triangle subdivision.
| 6 | | [![](./day_7/genuary_7.gif)](./day_7/day_7.js)
Generate some rules, then follow them by hand on paper.
| [![](./day_8/genuary_8.gif)](./day_8/day_8.js)
Curve only.
| [![](./day_9/genuary_9.gif)](./day_9/day_9.js)
Interference patterns.
| 7 | | [![](./day_10/genuary_10.gif)](./day_10/day_10.js)
`// TREE`
| [![](./day_11/genuary_11.gif)](./day_11/day_11.js)
Use a non-computer random source
| [![](./day_12/genuary_12.gif)](./day_12/day_12.js)
Use an API
| 8 | | [![](./day_13/genuary_13.gif)](./day_13/day_13.js)
Do not repeat.
| [![](./day_14/genuary_14.gif)](./day_14/day_14.js)
`// SUBDIVISION`
| [![](./day_15/genuary_15.gif)](./day_15/day_15.js)
Let someone else decide the rules.
| 9 | | [![](./day_16/genuary_16.gif)](./day_16/day_16.js)
Circles only
| [![](./day_17/genuary_17.gif)](./day_17/day_17.js)
Draw a line, pick a new color, move a bit.
| [![](./day_18/genuary_18.gif)](./day_18/day_18.js)
One process grows, another process prunes.
| 10 | | [![](./day_19/genuary_19.gif)](./day_19/day_19.js)
Increase the randomness along the Y-axis.
| [![](./day_20/genuary_20.gif)](./day_20/day_20.js)
No loops.
| [![](./day_21/genuary_21.gif)](./day_21/day_21.js)
`DRAW(x);`
| 11 | | [![](./day_22/genuary_22.gif)](./day_22/day_22.js)
Draw a line. Wrong answers only.
| [![](./day_23/genuary_23.gif)](./day_23/day_23.js)
**#264653** **#2a9d8f** ...
| [![](./day_24/genuary_24.gif)](./day_24/day_24.js)
500 lines
| 12 | | [![](./day_25/genuary_25.gif)](./day_25/day_25.js)
Make a grid of permutations of something.
| [![](./day_26/genuary_26.gif)](./day_26/day_26.js)
2D perspective
| [![](./day_27/genuary_27.gif)](./day_27/day_27.js)
Monochrome gradients without lines.
| 13 | | [![](./day_28/genuary_28.gif)](./day_28/day_28.js)
Use sound.
| [![](./day_29/genuary_29.gif)](./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/genuary_1.gif)](./day_1/day_1.js)
Perfect loop
| [![](./day_2/genuary_2.png)](./day_2/day_2.js)
Made in 10 minutes
| [![](./day_3/genuary_3.gif)](./day_3/day_3.pde)
Glitch Art
| 4 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | 5 | | [![](./day_4/genuary_4.gif)](./day_4/day_4.pde)
Intersections
| [![](./day_5/genuary_5.gif)](./day_5/day_5.pde)
Debug view
| [![](./day_6/genuary_6.gif)](./day_6/day_6.js)
Steal Like An Artist (credit: **Vahram Muratyan**)
| 6 | | [![](./day_7/genuary_7.png)](./day_7/day_7.js)
Sample a color palette from an album cover
| [![](./day_8/genuary_8.gif)](./day_8/day_8.pde)
Signed Distance Functions
| [![](./day_9/genuary_9.gif)](./day_9/day_9.pde)
Plants
| 7 | | [![](./day_10/genuary_10.gif)](./day_10/day_10.pde)
Generative Music
| [![](./day_11/genuary_11.gif)](./day_11/day_11.pde)
Suprematism
| [![](./day_12/genuary_12.gif)](./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 | genuary_10 12 | 13 | ## => [2023 Edition](./2023/README.md) 14 | 15 | genuary_1 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 | --------------------------------------------------------------------------------