Rotate towards mouse

For this sketch, we applied what we've learned in the previous exercises such as transformations and colour to create a pattern. The pattern consists of circles that rotate towards the current mouse position.

Setting Up the Canvas

The following are global variables needed to define the initial properties of the canvas and the circles that will be drawn later.

let shapes = [];
let canvasSize = 500;
let numOfShapes = 20;
let shapeSize = canvasSize/numOfShapes;
let col, d;

The empty shapes array is where the circles created from the Shape class will be added. canvasSize refers to the width and height of the canvas and numOfShapes is the number of circles drawn in each row and column on the canvas. shapeSize is the width and height of the circle. col is the colour of each circle and d stands for distance. These variables will be used in the Shape class.

function setup() {
  createCanvas(canvasSize, canvasSize);
  ellipseMode(CENTER);
  colorMode(RGB, numOfShapes, numOfShapes, numOfShapes);
}

As shown in the code above, the color mode is set to RGB and the range for the red, green, and blue are all set to the number of shapes on the canvas. This is for generating an interesting colour palette.

The Shape class

The circle objects that are drawn on the canvas are created from a class called Shape. A circle object has the following properties.

class Shape {
  constructor(_i, _j) {
    this.i = _i;
    this.j = _j;
    this.s = shapeSize;
    this.x = this.i * this.s + this.s/2; 
    this.y = this.j * this.s + this.s/2; 
    this.angle = 0;
  }
}

this.i and this.j are index numbers. this.s is initialised to the value assigned to the global variable shapeSize. this.x and this.y are offset properties that make sure the circles will be drawn within the canvas. this.angle is initialised to 0 but this value changes when the circles rotate towards the mouse.

Before drawing the circles on the canvas, I first populated the shapes array in setup().

for (let i = 0; i < numOfShapes; i++) {
  for (let j = 0; j < numOfShapes; j++) {
    shapes.push(new Shape(i, j));
  }
}

Drawing the Circles

I have a method called render() in the Shape class which draws a circle object on the canvas when called. Each circle has its own origin point which are the values assigned to the properties this.x and this.y. The colour of the outline of each circle depends on its position on the canvas.

render() {
  push();
  translate(this.x, this.y);

  col = color(this.i, 0, numOfShapes - this.j);
  
  noFill();
  stroke(col);
  ellipse(0, 0, this.s, this.s);

  pop();
}

Next, the render() method is called on each circle object in the draw() function. The result is 400 circles drawn on the canvas, each has no fill colour and has a different outline or stroke colour.

function draw() {
  background(0);
  shapes.forEach(shape => {
    shape.render();
  });
} 

Rotating Each Circle Towards Mouse Position

The method updateAngle() changes the inital angle of a circle object. It has the parameters _mx and _my. These parameters refer to the mouseX and mouseY variables that return the current mouse position on the screen. atan2() calculates the angle in radians from each circle to the mouse position. The updateAngle() method is added just before the render() method in the Shape class.

updateAngle(_mx, _my) {
  let dx = _mx - this.x;
  let dy = _my - this.y;
  this.angle = atan2(dy, dx);
}

The code below rotates a circle by the current angle value. It is included in the render() method after the translation.

rotate(this.angle);

Then the updateAngle() method is called on each circle object in the draw() function.

shapes.forEach(shape => {
  shape.updateAngle(mouseX, mouseY);
  shape.render();
});

Smaller circles are drawn on the canvas to make the rotation more obvious. The following lines of code are added in the render() method before pop().

fill(255);
noStroke();
ellipse(this.s/4, 0, this.s/2, this.s/2);
      

Changing the Size of Each Circle

To better the interactivity of the sketch, the size of each circle also changes according to the current mouse position.

d = dist(mouseX, mouseY, this.x, this.y);
this.s = map(d, 0, width, 10, shapeSize);

The dist() function calculates the distance between the mouse position and the circle position. The result is scaled from a range between 0 and the width to a range between 10 and the value assigned to the shapeSize variable.