class generativeArt { //class to generate art on canvas constructor() { this.move_art = -200; this.xRot = 0.01; //rotation speed about the x-axis this.yRot = 0.01;//rotation speed about the y-axis this.drawBox();// } drawBox() { //function to draw rotating intertwined boxes push();//give the box the below properties till it meets pop rotateX(frameCount * this.xRot); //rotate about x-axis rotateY(frameCount * this.yRot); //rotate about y-axis stroke(0); noFill(); box(3, 3, 30); //draw box //second box rotateX(frameCount * this.xRot); rotateY(frameCount * this.yRot); stroke(255, 255, 255); noFill(); box(3, 3, 30); pop(); //resets the box properties to its original properties } art() { //function to create rotating boxes at different locations on the canvas for (let i = 10; i < 1000; i += 50) { for (let j = 20; j < 1000; j += 100) { push(); translate(j - i, i); //translate the box to different positions determined by i and j this.drawBox(); pop(); } } for (let i = 10; i < 1000; i += 50) { for (let j = 20; j < 1000; j += 100) { push(); translate(i, j - i); //translate the box to different positions determined by i and j this.drawBox(); pop(); } } for (let a = -350; a < 350; a += 30) { push(); translate(a + 10, (this.move_art+= 0.001)); //translate the box to different positions determined by a but moves the boxes down the y axis this.drawBox(20, 50, 10); pop(); } } } function setup() { createCanvas(700, 400, WEBGL); background(220); my_art = new generativeArt(); } function draw() { my_art.drawBox(); my_art.art(); } //inspiration //from the previous homework created using boxes in 3D. //challenges faced: //I had a difficulty in navigating the 3D space difficulty in resetting the movement from the top of the canvas. //Also, it was a hustle figuring out how to make sure the boxes stay in a fixed position
Category: Spring 2023 – Shiloh
HW3: Generative Art Using OOPs and Perlin Noise
Inspiration
This code is inspired by a video from “The Coding Train” channel on YouTube. The video shows how to create a flow field simulation using particles that follow the flow of a vector field. It uses Object-Oriented Programming, arrays, and Perlin noise to create flow field particles that look very aesthetically pleasing.
Concept
The concept behind the flow field simulation is that particles move according to a vector field, which defines the direction and magnitude of the flow. The vector field is generated using noise, so that the flow is not predictable and creates a natural, organic look. The particles follow the flow field by calculating the angle of the vector at their current position, and adjusting their velocity and position accordingly.
Implementation
The implementation of the code involves creating a Particle class to represent each particle in the system. This class contains a number of methods to control the particle’s motion. The update()
method updates the velocity and position of the particle based on its acceleration. The follow()
method uses the particle’s current position to find the corresponding vector in the flow field and apply it as a force to the particle. The display()
method draws a line from the particle’s current position to its previous position, and the updatePrev()
method updates the previous position to be the current position. The resetParticles()
method checks if the particle has gone out of bounds and wraps it back to the other side of the canvas if it has.
The main setup function generates the flow field and creates the particles. The flow field is generated by dividing the canvas into a grid of cells, and then generating a random vector for each cell. The number of columns and rows in the grid is calculated based on the size of the canvas and the scale factor, s
. The setup()
function also creates the particles by creating new instances of the Particle class and adding them to the particles
array.
The draw()
function updates the flow field and the particles. First, it updates the noise offset to create a smoothly changing flow field. Then, it loops through the grid and calculates a new angle for each cell based on the noise function. This angle is converted into a vector, which is stored in the flowfield
array. Next, the function loops through the particles, and for each particle, it calls the follow()
method to apply the corresponding flow field vector to the particle. Then, the update()
method is called to update the particle’s velocity and position based on its acceleration. Finally, the display()
method is called to draw the particle and the resetParticles()
method is called to wrap the particle back to the other side of the canvas if it has gone out of bounds.
// Class for a particle that moves based on a perlin noise flow field class Particle { constructor() { // Initial position of the particle at a random point on the canvas this.pos = createVector(random(width), random(height)); // Initial velocity set to (0, 0) this.vel = createVector(0, 0); // Initial acceleration set to (0, 0) this.accel = createVector(0, 0); // Maximum speed that the particle can move this.maxV = 4; // Store the previous position of the particle this.prevPos = this.pos.copy(); } // Follow the flow field vectors follow(vectors) { // Calculate the grid position of the particle let x = floor(this.pos.x / s); let y = floor(this.pos.y / s); let index = x + y * cols; // Get the force vector at that grid position let force = vectors[index]; // Apply a force to the particle's acceleration this.accel.add(force); } // Update the position of the particle based on its velocity and acceleration update() { // Add the acceleration to the velocity this.vel.add(this.accel); // Limit the velocity to the maxV this.vel.limit(this.maxV); // Add the velocity to the position this.pos.add(this.vel); // Reset the acceleration to (0, 0) this.accel.mult(0); } // Display the particle as a line connecting its current and previous position display() { stroke(220); strokeWeight(1); line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y); this.updatePrev(); } // Update the previous position of the particle updatePrev() { this.prevPos.x = this.pos.x; this.prevPos.y = this.pos.y; } // Check if the particle has gone outside the canvas and wrap it around if necessary particleReset() { if (this.pos.x > width) { this.pos.x = 0; this.updatePrev(); } if (this.pos.x < 0) { this.pos.x = width; this.updatePrev(); } if (this.pos.y > height) { this.pos.y = 0; this.updatePrev(); } if (this.pos.y < 0) { this.pos.y = height; this.updatePrev(); } } }
Challenges
One of the challenges in this project was figuring out how to create a smooth flow field that changes over time. This was achieved by using the noise function to generate random values for each cell in the grid. By updating the noise offset in the draw()
function, the flow field smoothly changes over time, creating a dynamic and interesting visual.
Another challenge was figuring out how to apply the flow field vectors to the particles in a way that creates a believable and visually appealing motion. This was achieved by dividing the canvas into a grid of cells and using the particle’s position to find the corresponding flow field vector. The particle’s velocity is then updated based on this vector, creating a believable motion that follows the flow of the flow field.
Reflection
This project was a fun and interesting exploration of particle systems and flow fields. By creating a simple system of particles that follow a flow field, it is possible to create a wide variety of dynamic and visually appealing patterns. The use of the noise function to generate the flow field allows for a high degree of control over the motion of the particles and the overall visual appearance of the system. This project was a valuable learning experience, and it has given me a deeper appreciation for the potential of particle systems and flow fields for creating dynamic, organic and engaging visuals using object-oriented programming, arrays, and Perlin noise.
Reference
Sketch Link: https://editor.p5js.org/swostikpati/full/-KXM2Ag3s
Assignment 3 – OOP Generative Art
In this assignment, I decided to create an abstract form of art. I started off with generating a dynamic art piece featuring circles that appear and disappear from the screen over time. To make them appear prettier, I made them multi-colored, translucent, removed their stroke, and gave them different sizes. To give the circles these characteristics, I made a separate circle class to continuously generate more circles and then start to delete them once their number reached greater than a hundred.
Once the basic step was achieved, I wanted to make the canvas and art more interactive. I decided to make the circles bounce off the edges of the canvas when the space bar is pressed while the initial effect is still maintained. To do this, I made a move function in the circle class which is activated when the Space Bar is pressed. This effect was achieved by reversing the speed when the circles touch the edge. However, it took me a while to refine the if condition to ensure the bouncing effect was clearly understandable.
The circles start off randomly placed on the canvas and move and bounce off the edges when the space bar is pressed. When the mouse is clicked, all the circles form a line and start to spin around the center of the canvas. The swirling motion of the circles and the changing colors could represent a tornado or a whirlwind, creating an abstract and mesmerizing image. As we discussed chaos and randomness in class, I wanted to create an art piece which had both these elements. The dynamic motion of the circles creates a sense of chaos and unpredictability, while the synchronized spinning adds a touch of order and harmony.
PRESS SPACE AND MOUSE CLICK TO INTERACT
One challenging aspect of this code is the mathematical calculation involved in the spinAround method. This method uses trigonometry to update the position of the circle based on its angle, ensuring that it spins smoothly around the center of the canvas. After trying multiple different way of making the circles spin, with the help of this formula that I found online, I was able to achieve the spinning effect. Keeping track of the angle, position, and movement of each individual circle is also a challenge, but the use of classes and arrays helped to solve this problem. I really enjoyed making this art piece and therefore kept adding more interactivity to better understand these fundamentals.
OOP – Aigerim
Inspiration
For this assignment, I recreated my own version of the Rock, Paper, Scissors game I saw on TikTok. Having spent a little too much time watching it, I wanted to create my own version of it that is scalable and in which the simulation can be played over and over again.
The Product
Overall, I am very happy with the outcome. The code design underwent many changes as I added new features and removed the useless ones, for example, I started off with 3 different classes for Rock, Paper, and Scissors but realized that the code was a bit redundant and reduced it to one Obj class.
Challenges
//function to check for collision of two objects (thisO and all the other objects) function checkCollision(thisO) { for (let i = 0; i < obj.length; i++) { //check the distance between the two objects let d = dist(thisO.x, thisO.y, obj[i].x, obj[i].y); //if the two objects are within close proximity and are not the same apply the rock paper scissor rules if (d <= thisO.size / 2 + obj[i].size / 2) { //thisO.dx = -thisO.dx; //thisO.dy = -thisO.dy; //obj[i].dx = -obj[i].dx; //obj[i].dy = -obj[i].dy if (obj[i].id == "📄" && thisO.id == "🗿") { thisO.id = "📄"; } else if (obj[i].id == "✂️" && thisO.id == "📄") { thisO.id = "✂️"; } else if (obj[i].id == "🗿" && thisO.id == "✂️") { thisO.id = "🗿"; } } } }
One of the biggest challenges was figuring out how to detect the collision between my objects, for which I ended up calculating the distance between all the objects at a given time and checking if that distance is less than the radius of my object. Another challenge is to make the collisions smooth without having two objects cross each other, which I am yet to work out. Apart from that, it was a really fun project to work on, and very entertaining to see which one will win every time I run it!
Assignment 3: Generative Art!
Concept
For the Third Assignment, my aim was to incorporate everything we have learned in class such as Arrays, collisions, and Classes, and use it to create an engaging piece of art. Initially, I planned to create a intricate animation, but realized that it was beyond the scope of the assignment. So, I settled on creating an animation that would pique the viewer’s interest and leave them wondering about how it was created.
I started off with replicating an in-class example of dropping snowflakes, which were reproduced, and I thought of replacing that with colored circles over a black background. This did look interesting, but seemed a bit basic – the circles were originally only rebounding from the walls (edges of the canvas). So I developed it further such that they collide with each other and exchange their speeds to seem interesting. Then, developing it further, I added a Neon Image on top of it and exchanged the images on collision as well.
I achieved a mixture of Chaos, but order. Everything had its set path – an order to follow. However, a lot of order among multiple objects that were not coordinated amongst each other caused Chaos. This is something I drew as an inspiration from an in-class discussion.
Embedded Canvas
The assignment outcome is attached as an embedded sketch as follows:
Try pressing the ‘m’ key! (In case it does not work, make sure to have clicked on the canvas)
Code
// Note: Press 'm' to view a messy functionality let circles = []; //Array to store the circles let noCircles = 20; //Number of circles that appear on the screen let messup=false; //To enable the messy functionality //Function to preload the graphics. This would load all the graphics before the setup function is run function preload() { purple_img = loadImage('purple.png'); green_img = loadImage('green.png'); red_img = loadImage('red.png'); } function setup() { createCanvas(400, 400); // Create objects of the class Circle and store them in an array for (let i = 0; i < noCircles; i++) { // A random positioning and size of each circle let r = random(10, 30); let x = random(0, width-2*r); let y = random(0, height-2*r); // Check if a circle with the same position and size already exists let overlap = false; for (let j = 0; j < circles.length; j++) { let distance = dist(x, y, circles[j].x, circles[j].y); if (distance < r + circles[j].r) { overlap = true; break; } } // If there is no overlap, create a new circle if (!overlap) { circles[i] = new Circle(x, y, r); } else { i--; } // The above code blocks avoid any colliding circles being drawn in the initial stages } } function draw() { background(0); // Display objects in the array and update their position for (let i = 0; i < circles.length; i++) { circles[i].update(); circles[i].display(); // Check for collisions between circles for (let j = i + 1; j < circles.length; j++) { if (circles[i].collidesWith(circles[j])) { // If the messup functionality has been set, a different collision resolve function is called if (messup==true){ circles[i].resolveCollisionWeird(circles[j]); } else{ circles[i].resolveCollision(circles[j]); } } } } } // Circle object class Circle { constructor(x, y, r) { this.x = x; this.y = y; this.r = r; this.speed = random(1, 5); this.dx = random(-this.speed, this.speed); this.dy = random(-this.speed, this.speed); // A Number that checks the image_type to choose from the 3 types that we have this.image_type = int(random(1,4)); } display() { // The circle image is displayed, depending on the image it got allotted in the constructor if(this.image_type%3==1){ image(green_img, this.x, this.y, this.r*2, this.r*2); } else if(this.image_type%3==2){ image(purple_img, this.x, this.y, this.r*2, this.r*2); } else{ image(red_img, this.x, this.y, this.r*2, this.r*2); } } update() { this.x += this.dx; this.y += this.dy; // Check for collisions with the edges of the canvas if (this.x + this.r*2 > width || this.x < 0) { this.dx = -this.dx; } if (this.y + this.r*2 > height || this.y < 0) { this.dy = -this.dy; } } // Checks for collision with one another collidesWith(other) { // Calculates the distance between the current object and other objects let distance = dist(this.x, this.y, other.x, other.y); // If it is less than a threshold, it has collided! return distance < this.r + other.r; } // Once a collision is detected from the above function, the following function is called. It changes the speed of each, changes the image type with the other resolveCollision(other) { let tempX = this.dx; let tempY = this.dy; this.dx = other.dx; this.dy = other.dy; other.dx = tempX; other.dy = tempY; other.image_type = this.image_type; } // The following block of code was a modification of the above function which did not seem to work as I wanted it to, however it actually did something more artsy and interesting that I wanted to keep it as a hidden functionality to play with! resolveCollisionWeird(other) { let angle = atan2(other.y - this.y, other.x - this.x); let overlap = (this.r + other.r) - dist(this.x, this.y, other.x, other.y); this.x += cos(angle) * overlap / 2; this.y += sin(angle) * overlap / 2; other.x -= cos(angle) * overlap / 2; other.y -= sin(angle) * overlap / 2; let tempX = this.dx; let tempY = this.dy; this.dx = other.dx; this.dy = other.dy; other.dx = tempX; other.dy = tempY; } } // In the case that the key 'm' is pressed, the collision works differently. function keyPressed(){ if (key == 'm'){ messup=true; } }
Problems
A small problem that I ran into was an issue where some of the images were colliding up to a point where an overlap could be observed. Since I started off with ellipses instead of the images, I was using their centre as a reference point, however, changing that to an image caused a shift in the reference point, which was now the top left of the image. This caused multiple issues including the one mentioned in the beginning of this paragraph.
It may not be noticeable to everyone, however I tried to resolve the issue and instead came up with something chaotic. You can press ‘m’ to see that!
The full canvas is linked here.
Assignment 3: Tetris
This program’s code carries out a straightforward falling-blocks game.
In order to avoid colliding with other blocks or the canvas’s bottom, the user must use the arrow keys to move the blocks to the right or left as they constantly fall from the top of the canvas in this piece of art. A block becomes a permanent block and a new block begins to fall when it collides with another block or reaches the bottom of the canvas. The game continues until all of the blocks have reached the top of the canvas or until it becomes impossible to construct new blocks because they would clash with existing ones.
A class called “Block” and numerous functions make up the code. The class is used to specify each block’s characteristics and behavior. When the game first begins, the canvas is initialized and the first block is started by using the “setup” method once. The lines of the matrix are drawn on the canvas via the “matrix” function. The “draw” function is used repeatedly in a loop to update and depict the game’s current state. The location of the falling block is updated using the “updateFallingBlock” function. All the blocks on the canvas are drawn using the “drawAllBlocks” function. A new block is created via the “startNewBlock” function. When the user taps an arrow key to move the falling block, the “keyPressed” function is invoked. If the user clicks the mouse to restart the game after it has ended, the “mouseClicked” method is invoked.
The main issue is that blocks may sometime collide with each other when the keys are pressed. The part of checking if there is a block already placed adjacently was a bit complicated. This is something I can work on in the future. Another issue is that there is no method to record the score or the time in the game. It is not too complicated to add the scoreboard, but I decided to focus on the OOP mainly on this Assignment.
Majid Week 3 – SURVIVE
Concept:
My concept this week is “SURVIVE”. It is an interactive video game in which the player is a triangle located at the cursor and the objective is to avoid incoming projectiles RGB colored projectiles. The text at the top includes a timer and a health counter. The timer is 30 seconds long, if the user survives for the amount of time without losing all his/her health the game displays a “MISSION ACCOMPLISHED” page. If the player loses all his Health before the timer runs out then the game will display a “GAME OVER” screen.
Embedded:
Code:
In this week’s assignment, I had 2 main issues, which were keeping track of time and managing the projectile collision. The first snippet is the method I used to keep track of time. I discover a setinterval() function in js which allows the user to set a specific amount of time in order to increment, which I set to 1000 ms or 1s. I created another function that counts down the timer and displays a console message when the timer ran out for debugging purposes. I then used the timer variable to dictate what happens in the game.
The collision aspect was interesting because it requires precision or else the player experience for the game would not be great. I created 2 functions that calculated the triangle area and checked whether the inputted coordinates are within that triangle. In order to make this work for the cursor triangle, I inputted the coordinates of 3 points surrounding the cursor which combined make up a triangle. This allowed me to get precise collision detections for the triangle which is located on the cursor.
Future Improvements:
There are quite a lot of improvements that can be made to this assignment. For example, I can add different levels which scale in difficulty. The difficulty would be dictated by the speed of the incoming projectiles as well as the number of them. Another way is to have the speed of the projectiles scale with the timer, and there would be a points system based on the timer as well. This setup is similar to how the classic game Tetris works in which the longer the player lasts the higher their score. Adding sound effects would also enhance the player’s overall experience as well. This assignment has the potential to be a fully-fledged game in the future.
HW3: Generative Art using OOP
CONCEPT
I was inspired by the Solar System, specifically by Saturn. I wanted to draw a planet in the night sky, however, I don’t really know how to do smooth gradients and complex coloring in p5.js, so instead, I took the shape of one of the most easily recognizable planets to create my own version of it.
IMPLEMENTATION
My sketch may not look too complicated, but it took me a long time to figure it out. The main challenge was “filling” the body of the planet with parallel lines. I thought that I could imitate volume by drawing a circle out of parallel ellipses. However, I didn’t know how to make them change their width in a smooth way so as to form a circle (look at the reference image below). I have tried checking the coordinate ranges with if-statements and manipulating X points of the ellipses there, but it didn’t quite behave as I expected it to. I then moved on to using lines instead, expecting that it could be easier to implement, which it was, but it took me at least 10 minutes of staring at the screen expecting to recall something useful from math classes. The answer to my question was the Pythagorean theorem, where c^2 = a^2 + b^2. In my case, I have c and a and am looking for b, so the function that I have used to find X_1 and X_2 for a line was:
//use Pythagorean theorem to find point X_n function differenceOfSquares(num1, num2) { let diff = num1 * num1 - num2 * num2; return sqrt(abs(diff)); }
My sketch uses two classes to build planet body stripes and planet rings. Sketch elements are drawn in the next order: stars -> planet body -> planet rings -> arc to cover the back side of planet rings -> planet rings. I found it to be the easiest scheme. Planet rings and body rings have random red values in the stroke color, and random stroke weight (0 or 1) to imitate rotation. Planet rings have a wider range of red values than body rings. I figured that it look better this way. On top of the rings, there are floating rocks drawn as points with random stroke weights and coordinates. These rocks make the planet feel more alive and less plain. Rotating the whole thing was easier this time, now that I know that rotate function uses radians and not degrees as I assumed when working on my first homework submission. The frame rate is 8, which is fast enough to feel like the planet is rotating and slow enough to see the details and get overwhelmed by the picture.
ASPECTS TO WORK ON
There is a feeling of movement in the sketch. However, I can’t tell where or how exactly it is moving. Everything is “rotating” at the same time at the same speed, making me feel disoriented. I feel like I could have used Perlin noise for the sky and the planet’s body stripes to make the picture move smoother. I also feel like there could have been some text there because it gives me wall poster vibes but that’s just a thought.
Week 3 – recreating my pattern with classes
For this week we had to use the artwork from last year and incorporate classes into the code. Since I already explained my concept I will write it down very briefly here:
I was inspired to recreate one of the examples shown in class, and I did a series of 4 pieces of art which related to geometric patterns and symmetry as the one below. This one shows a different pattern each time the code is run, hence creating a new piece of art each time.
Since I revised last weeks’ work, there was no difference in the artwork (because that was the intention), there was, however, quite a difference in the code. I am a lot more pleased with the revised code, as it is easier to read and looks cleaner – grouping most of the repetitive things under the class, creating the squares in an array and keeping the setup() function cleaner.
class squareFiller { //constructor consists of the x and y coordinates constructor(x, y) { this.x = x; this.y = y; }
The constructor is very simple with just the coordinates as attributes.
//the first option of a pattern //the x and y coordinates are parameters as they will //be determined later in setup() //k variable determines how spaced each square will be //from each other //the while loop determines the pattern by incrementing //and decrementing respectively each of the variables //the rest of the fillers have the same idea but with //different spacing and corners which they approach filler1(x, y) { var i = x; var j = y; var k = 100; while (i < x + 60) { square(i, j, k); i += 10; j += 10; k -= 15; } }
This is the first of 12 similar filler pattern functions.
randomizer() { let num = floor(random(12)) + 1; print(num); if (num == 1) { this.filler1(this.x, this.y); } else if (num == 2) { this.filler2(this.x, this.y); } else if (num == 3) { this.filler3(this.x, this.y); } else if (num == 4) { this.filler4(this.x, this.y); } else if (num == 5) { this.filler5(this.x, this.y); } else if (num == 6) { this.filler6(this.x, this.y); } else if (num == 7) { this.filler7(this.x, this.y); } else if (num == 8) { this.filler8(this.x, this.y); } else if (num == 9) { this.filler9(this.x, this.y); } else if (num == 10) { this.filler10(this.x, this.y); } else if (num == 11) { this.filler11(this.x, this.y); } else { this.filler12(this.x, this.y); } }
The randomizer function randomly chooses one of the patterns for the square at hand.
//instantiating the objects in the array for //each square in the grid for(let i = 0; i<601; i+=100) { for(let j =0; j<301; j+=100) { arr.push(new squareFiller(i,j)); } } //calling the randomizer function for each //square in the array for(let i=0; i<arr.length; i++) { arr[i].randomizer(); }
The setup() function is quite simple, with these lines being the most important as they instantiate the SquareFiller objects in an array, then there is another for loop which calls the randomizer function for each of the objects – hence choosing a pattern for each square in the canvas.
The biggest challenge for me was thinking about how I was going to incorporate a class into this code. But after a bit of pondering and experimenting I decided to create a SquareFiller class, which determined the pattern of each square.
Furthermore, I faced the challenges of writing a class in JavaScript as I had never done that before. To solve this, I referred to the class notes, used the Reference of P5 and the internet and figured everything out.
Week 3 – Object Oriented Programming Art
Concept:
Object Oriented Programming (OOP) and art are two separate concepts to me. I have only ever used it to create games, or different data structures, so when it came to creating art, I was in an art block. Since OOP is extremely useful when it comes to motion of objects and graphics on the screen, I was immediately drawn to something dynamic. But what? Since all of my friends are taking Foundations of Science, I am continuously surrounded by physics. This month, the topic was circular motion. When I think about circular motion, I think about rotation and revolution of celestial bodies. Hence, for this week’s project, I have created a small work that depicts the motion of planets (Mercury through Neptune sorry Pluto 🙁 ) around the Sun.
The Solar System
Link: https://editor.p5js.org/ajlasacic/full/GIWVhC73H
Challenges:
To start off, circular motion was hard to think about when working with OOP. Since I was working with many orbits, it was a struggle to spread them adequately across the screen. The model is obviously scaled down, it is an artistic representation of the system, but managing the distances between the planets and setting up the asteroid belt was really tricky and took some fiddling to get right.
this.display = function () { fill(this.color); ellipse(this.x, this.y, this.radius); }; } // Move objects in a circle by updating the angle move() { this.x = this.xPos + this.distance * cos(this.angle / this.speed); this.y = this.yPos + this.distance * sin(this.angle / this.speed); this.angle++; }
The asteroid belt was another point of concern. I wanted to contain it in one place, so that it can be considered one object. Although I could have just used for loops outside of the code, I created another class which made it easier to control the specific parameters of the belt such as the size and number of circles.
// Asteroid belt class Belt { constructor(m, n) { // Number of belts this.n_belts = m; // Number of asteroids in belt this.n_rocks = n; // Array for storing asteroid objects this.beltArray = []; } // Produces a 2d array - this.beltArray[] add_asteroids() { for (let i = 1; i <= this.n_belts + 1; i++) { let belt = []; for (let j = 1; j <= this.n_rocks; j++) { belt[j] = new Celestial( width / 2, height / 2, random(2, 4), i * (start - 10) + 170, j * 130 + i * random(10, 15), random(12.5, 37.5) * rotation, random(100, 200) ); } this.beltArray.push(belt); } } // Displays each meteor by calling Celestial display_meteors() { for (let i = 1; i <= this.n_belts; i++) { for (let j = 1; j <= this.n_rocks; j++) { this.beltArray[i][j].move(); this.beltArray[i][j].display(); } } } }
Unfortunately, when I came to that point, my code got deleted. In all honesty, it was my fault since I did not save the file once before leaving my workstation. From then on, it took another couple of hours to get back to my original progress, but I was devastated for quite some time because a split second was all it took to lose hours of work.
I was struggling to create the trail effect. In the beginning, I was trying to write more code in order to make it appear behind the celestial object. In the end, I realized that if I decrease the alpha of the background, a nice trail should be left behind by the celestial objects.
// Opacity can change for trail effect of moving objects background(0,0,0, opacity);
Finally, in order to introduce some creativity, I wondered whether an orbit can be drawn for each planet. For some time I tried to create it within the class, but since my Belt class creates instances of the Celestial class, it would make an orbit for each asteroid as well. I settled for creating a separate orbit() function that uses the start variable to size the orbits. Unlike C++ I could not make getter functions, so I could not extract radii for the planets which is why I ultimately could not make everything resizable .
function orbits() { // Sun glow for (let i = 0; i < 50; i++) { fill(241, 93, 34, 3); ellipse(width / 2, height / 2, i * 1.3); } noFill(); stroke(255, 255, 255, val); ellipse(width / 2, height / 2, start * 4); ellipse(width / 2, height / 2, start * 6); ellipse(width / 2, height / 2, start * 10); ellipse(width / 2, height / 2, start * 14); ellipse(width / 2, height / 2, start * 24); ellipse(width / 2, height / 2, start * 28); ellipse(width / 2, height / 2, start * 32); ellipse(width / 2, height / 2, start * 37); noStroke(); } function mousePressed() { val = 20; } function mouseReleased() { val = 0; }
Reflection
Overall, I am satisfied by the look of the project. I altered some CSS parameters to make the canvas corners rounded which gives a nice effect to the piece. Although we can intuitively realize that this is our solar system, I wished to add more detail to the planets. For example, Saturn’s rings and Earth’s life, i.e., greenery. Unfortunately, since I lost all of my progress and had to trace my steps in order to remake the piece, I was left with little time to try and achieve that. Although I could have created a Saturn class, I wanted to make use of OOP and use one class for all planets. I am very happy with the trail and glow effect I created by utilizing the alpha variable of fill. In the future, I wish to create pieces that are more artistic, perhaps expressionist, but since I am not used to combining art with OOP, I know it will take some time.