Assignment 2 – The Heliocentric Model

I sometimes find it crazy that we live on a planet. Out of billions of celestial objects in the universe, we exist on this beautiful sphere (not technically) – Earth.

This is the moving object I decided to use, because everything that exists in our reality belongs to this planet. The motion on this project involves a depiction of the Heliocentric modal of the universe, proposed by Nicolaus Copernicus in the 16th century, that asserts that the Sun is at the center of our solar system, with the planets, including Earth, orbiting around it.

However, the earth doesn’t just orbit the Sun. It also rotates on its own axis, which happens to be tilted ~23 degrees. This truly extraordinary fact of the universe, which took humans thousands of years to realize, is what is depicted in the sketch.

An interesting part of the sketch is the fabric of cosmos. It is a metaphorical concept often used in cosmology and physics to describe the underlying structure of the universe. It basically means that space itself is not empty but is woven with the fabric of spacetime, a four-dimensional framework where all matter and energy exist and interact. I wish I could show all four dimensions in this project, but this is what I could build in a short span of time. Oh wait, that could be a future improvement for the project!

Here’s the sketch. You can interact with it as well – try to hold-click and drag using your mouse.

The tiny green sphere is for a point of reference. It shows that the earth is tilted and is rotating around an axis.

As mentioned before a cool part of this project is the Fabric. Here’s how I was able to implement it.

class Fabric {
  constructor(rows, cols, spacing) {
    this.rows = rows;
    this.cols = cols;
    this.spacing = spacing;
    this.points = [];

    // create an array of PVectors to represent points in the fabric
    for (let y = 0; y < this.rows; y++) {
      for (let x = 0; x < this.cols; x++) {
        this.points.push(createVector(x * this.spacing - this.cols * this.spacing / 2, y * this.spacing - this.rows * this.spacing / 2, 0));
      }
    }
  }

  display() {
    push();
    stroke(255);
    strokeWeight(0.5);
    noFill();
    for (let i = 0; i < this.points.length; i++) {
      vertex(this.points[i].x, this.points[i].y, this.points[i].z);
    }
    pop();
  }
}

Reading Response 1 – Ayush

As someone studying computer science, I find the concept of reductionism very intriguing. It’s similar to how we pursue the understanding of first principles in computing – breaking down complex problems into their fundamental components. This text demonstrates how various scientific disciplines, from biology to physics, follow a similar approach, dissecting the universe into smaller, more manageable parts. However, it also reminds us that, much like in computer science, merely understanding these building blocks isn’t enough. We must delve into the interactions and emergent properties that arise when these components come together, mirroring the complexity we often encounter in computational systems.

The part about fractals and chaos in this text reminded me of my past experiences. It’s like when I work with technologies that don’t always give the same results for the same inputs. A tiny change can make a big difference, just like chaos theory shows. Also, when the text talks about complex systems that learn and adapt, it’s a lot like what we do in machine learning and artificial intelligence. We teach computer programs to get better at tasks over time. So, this text reminds me that we should not just learn the basics; we should also explore how different parts work together, kind of like how scientists study the universe, from big bang to the multiverse.

Assignment 1 – Ayush

 

My inspiration for this project comes from this video by Veritasium.

Pathfinding have a ubiquitous presence in the real world. Their applications range from everyday navigation tools like Google Maps and logistics optimization for companies like Amazon. They are also used in guiding the movements of autonomous vehicles.

Through this project I wanted to experiment with more than just a random walk. I wanted to see if we can put some brain into our walker, so it doesn’t just go towards random directions, but makes some decision in order to find the destination.

Here’s the p5.js sketch:

https://editor.p5js.org/ap6178/sketches/Wi7864pjq

 

The walker performs multiple succeeding walks, from one cell to the other. But then it realizes that the direction it’s taking is not the correct one. When this happens, it tries a new path. Each of these paths have some metrics that tell the walker whether it’s going towards the correct direction or not.

To put brain into the walker, I have used the A* search algorithm. It basically takes two things into consideration in order to be able to make decisions:

  • The cost it took for the walker to get to the current point.
  • An estimate of how far the walker thinks it is from the destination.

This estimate is called the heuristic function in A* search algorithm. The total estimated cost is given by the following equation:

f(n) = g(n) + h(n)

At any node n, the estimated cost of the path is calculated i.e. f(n)
f(n) requires two other costs: the cost to reach node n i.e. g(n) and the estimated cost to reach the goal node i.e. h(n)

h(n) in this equation is called the heuristic function. There are many heuristic functions we can use, but in this project, I decided to use the Manhattan distance heuristic since we’re dealing with cells on the grid. The Manhattan distance is the sum of horizontal and vertical cell distances to the goal node.

This idea of calculating heuristic for each step makes the walker intelligent.

 

Code

Each node has the following properties as described by the Node class.

class Node {
  constructor(i, j) {
    this.i = i;
    this.j = j;
    this.f = 0;
    this.g = 0;
    this.h = 0;
    this.wall = random(1) < 0.30; // there's a 30% chance of being a wall
    this.previous = undefined;
  }

  show(col) {
    fill(col);
    if (this.wall) {
      fill(59, 25, 25);
    }
    rect(this.i * cellSize, this.j * cellSize, cellSize, cellSize);
  }

  getNeighbors() {
    const neighbors = [];
    const i = this.i;
    const j = this.j;

    if (i < cols - 1) {
      neighbors.push(grid[i + 1][j]);
    }
    if (i > 0) {
      neighbors.push(grid[i - 1][j]);
    }
    if (j < rows - 1) {
      neighbors.push(grid[i][j + 1]);
    }
    if (j > 0) {
      neighbors.push(grid[i][j - 1]);
    }

    return neighbors;
  }
}

The main brain of the walker lies on this heuristic measure. It calculates the Manhattan distance between two nodes nodeA and nodeB required for our estimation function.

function heuristic(nodeA, nodeB) {
  // Manhattan distance (sum of horizontal and vertical distances)
  const dx = abs(nodeA.i - nodeB.i);
  const dy = abs(nodeA.j - nodeB.j);
  return dx + dy;
}

With the help of this heuristic, the function values: f, g, and h are then updated and required actions are taken.

// check neighbors and update their f, g, h values
const neighbors = current.getNeighbors();
for (let neighbor of neighbors) {
  if (!visited.includes(neighbor) && !neighbor.wall) {
    const g = current.g + 1;
    let newPath = false;
        
    if (unexplored.includes(neighbor)) {
      if (g < neighbor.g) {
        neighbor.g = g;
        newPath = true;
      }
    } else {
      neighbor.g = g;
      unexplored.push(neighbor);
      newPath = true;
    }
        
    // update values if path is new
    if (newPath) {
      neighbor.h = heuristic(neighbor, endNode);
      neighbor.f = neighbor.g + neighbor.h;
      neighbor.previous = current;
    }
  }

Future Improvements

An improvement to this project would be to be able to connect the path with lines and show a more artistic version of how the walker is choosing new paths. I would also want to add some kind of animation when it reverts back a few steps to continue to a new path.