Hi, I am new in p5.js. Can someone help to make this work?

I have thee pentagons and I want to make it interactive, so when I hover with mouse to the center of the image, the pentagons will start to rotate quicker. Thank you for any advice.

function setup() { createCanvas(windowWidth, windowHeight); //createCanvas(600, 400); smooth(); }

function draw() { background(255); push(); stroke(230); noFill(); translate(windowWidth/2, windowHeight/2); rotate(frameCount / -461.0); polygon(0, 0, 515, 5); pop();

push(); stroke(200); noFill(); translate(width/2, height/2); rotate(frameCount / -263.0); polygon(0, 0, 313, 5); pop();

push(); stroke(170); noFill(); translate(width/2, height/2); rotate(frameCount / -153.0); polygon(0, 0, 203, 5); pop();

push(); stroke(140); noFill(); translate(width/2, height/2); rotate(frameCount / -92.0); //*34 polygon(0, 0, 142, 5); pop();

push(); stroke(110); noFill(); translate(width/2, height/2); rotate(frameCount / -58.0); polygon(0, 0, 108, 5); pop();

push(); stroke(80); noFill(); translate(width/2, height/2); rotate(frameCount / -29.0); polygon(0, 0, 79, 5); pop();

push(); stroke(50); noFill(); translate(width/2, height/2); rotate(frameCount / -23.0); polygon(0, 0, 70, 5); pop();

push(); stroke(20); noFill(); translate(width/2, height/2); rotate(frameCount / -20.0); polygon(0, 0, 60, 5); pop();

push(); stroke(0); noFill(); translate(width/2, height/2); rotate(frameCount / -17.0); polygon(0, 0, 58, 5); pop(); }

function polygon(x, y, radius, npoints) { var angle = TWO_PI / npoints; beginShape(); for (var a = 0; a < TWO_PI; a += angle) { var sx = x + cos(a) * radius; var sy = y + sin(a) * radius; vertex(sx, sy); } endShape(CLOSE); }

Answers

  • edited March 2018

    Since you want to do something differently depending on the position of the mouse, it is useful to know where the mouse is.

    Thankfully, we know where the mouse is because we have the built-in variables mouseX and mouseY that always have the X and Y coordinates of the mouse.

    We don't actually want the position of the mouse though. We want to do something based on the distance that the mouse is from the center of the sketch. Thankfully, we have the dist() function that allows us to easily calculate distances.

    The dist() function takes four parameters, which describe two points. The two points we care about are the position of the mouse and the center of the sketch, so it will be useful to calculate that distance:

    var distance = dist( mouseX, mouseY, width/2, height/2 );
    

    We don't actually want the distance though. We want to adjust rotations based on if the mouse is near or far from the middle. So it might be good to map this distance we have from the range 0 to... something... to 0 to 1. What's the something? Well, it's the maximum possible distance we could get, or:

    var max_distance( 0, 0, width/2, height/2 );
    

    And then we can use these values to get a multiplier:

    var mult = map( distance, 0, max_distance, 0, 1 );
    

    Now we could use this is our rotations... but that means replacing it in all our rotation calls.

    Instead, let's DRY out your code. DRY stands for Don't Repeat Yourself. Since all of your shapes are doing a rotation, it makes a lot of sense to move that rotation amount to be a parameter to your polygon() function:

    function polygon(x, y, radius, npoints, rotation ) { 
    

    Actually, since all your polygons are centered anyway, we don't need the x and y parameters either...

    function polygon(radius, npoints, rotation) {
    

    Thus we can massivley simplify your code:

    function setup() { 
      createCanvas(windowWidth, windowHeight);
      smooth();
    }
    
    function draw() { 
      background(255); 
      noFill();
      translate(windowWidth/2, windowHeight/2); 
    
      stroke(230); 
      polygon(515, 5, frameCount / -461.0); 
    
      stroke(200); 
      polygon(313, 5, frameCount / -263.0); 
    
      stroke(170); 
      polygon(203, 5, frameCount / -153.0); 
    
      stroke(140); 
      polygon(142, 5, frameCount / -92.0); 
    
      stroke(110); 
      polygon(108, 5, frameCount / -58.0); 
    
      stroke(80); 
      polygon(79, 5, frameCount / -29.0); 
    
      stroke(50); 
      polygon(70, 5, frameCount / -23.0); 
    
      stroke(20);  
      polygon(60, 5, frameCount / -20.0); 
    
      stroke(0);  
      polygon(58, 5, frameCount / -17.0); 
    
    }
    
    
    function polygon(radius, npoints, rotation) { 
      var angle = TWO_PI / npoints;
      rotate( rotation *
       (
         1 - map(
           dist(mouseX,mouseY,width/2,height/2), 0,
           dist(0,0,          width/2,height/2), 0, 1
         )
       ) 
      );
      beginShape(); 
      for (var a = 0; a < TWO_PI; a += angle) { 
        var sx = cos(a) * radius; 
        var sy = sin(a) * radius; 
        vertex(sx, sy);
      } 
      endShape(CLOSE);
    }
    
  • "BUT WAIT!" you exclaim, "That code looks likes like it is still WET (Write Everything Twice)!" And you're right. It still has many calls stroke() and polygon() that happen in a predictable pattern. We can fix that - DRY it out - with a loop!

    var radiuses = [ 515, 313,203,142,108,79,70,60,58 ];
    var rotations = [ -461.0, -263.0, -153.0, -92.0, -58.0, -29.0, -23.0, -20.0, -17.0 ];
    
    function setup() { 
      createCanvas(windowWidth, windowHeight);
      smooth();
    }
    
    function draw() { 
      background(255); 
      noFill();
      translate(windowWidth/2, windowHeight/2); 
      for( var i = 0; i < 9; i++){
        stroke(constrain(230-20*i,0,256));
        pentagon( radiuses[i], rotations[i] );
      }
    }
    
    function pentagon(radius, rotation) { 
      var angle = TWO_PI / 5;
      rotate( ( frameCount / rotation ) *
       (
         1 - map(
           dist(mouseX,mouseY,width/2,height/2), 0,
           dist(0,0,          width/2,height/2), 0, 1
         )
       ) 
      );
      beginShape(); 
      for (var a = 0; a < TWO_PI; a += angle) { 
        var sx = cos(a) * radius; 
        var sy = sin(a) * radius; 
        vertex(sx, sy);
      } 
      endShape(CLOSE);
    }
    
  • edited March 2018 Answer ✓

    "WAIT WAIT WAIT! HOLD ON!" you exclaim, "Something is not right here. When I move the mouse the pentagons spin very rapidly! that is NOT what I want..."

    Ah, you have noticed. We don't want the AMOUNT of rotation to depend on the mouse's distance from the center. We want the SPEED of rotation to depend on it.

    We could just fix this by adding another array - to track the amount each has rotated - but then we would have three different arrays... Four if you count the stroke colors. It would make a lot more sense to create a class that defines what a pentagon is...

    function Pent( stroke_val, radius, max_rotation_speed ) {
      this.stk = stroke_val;
      this.rad = radius;
      this.ro = 0;
      this.dro = max_rotation_speed;
      this.draw = function() {
        stroke( this.stk );
        noFill();
        this.ro += ( frameCount / this.dro ) * ( 1 - dist(mouseX,mouseY,width/2,height/2)/dist(0,0,width/2,height/2) );
        var angle = TWO_PI / 5;
        rotate( this.ro );
        beginShape();
        for (var a = 0; a < TWO_PI; a += angle) { 
          var sx = cos(a) * this.rad; 
          var sy = sin(a) * this.rad; 
          vertex(sx, sy);
        } 
        endShape(CLOSE);
      };
    }
    
    var radiuses = [ 515, 313,203,142,108,79,70,60,58 ];
    var rotations = [ -461.0, -263.0, -153.0, -92.0, -58.0, -29.0, -23.0, -20.0, -17.0 ];
    var pents = [];
    
    function setup() { 
      createCanvas(windowWidth, windowHeight);
      for( var i = 0; i < 9; i++ ) pents.push( new Pent(constrain(230-30*i,0,256), radiuses[i], rotations[i] ) ); 
      smooth();
    }
    
    function draw() { 
      background(255); 
      translate(windowWidth/2, windowHeight/2); 
      for( var i = 0; i < 9; i++ ) pents[i].draw();
    }
    

    This makes it very easy to add the new "array" (which is just a variable in the class) for the rotation amount (this.ro).

    "WAAAAAAIIIITT!" They still spin like crazy!

    Well yeah, because now their SPEED is way high. We can tinker with some values...

    function Pent( stroke_val, radius, max_rotation_speed ) {
      this.stk = stroke_val;
      this.rad = radius;
      this.ro = 0;
      this.dro = max_rotation_speed;
      this.draw = function() {
        stroke( this.stk );
        noFill();
        this.ro -= 1/2000 * this.dro  * ( 1 - dist(mouseX,mouseY,width/2,height/2)/dist(0,0,width/2,height/2) );
        var angle = TWO_PI / 5;
        rotate( this.ro );
        beginShape();
        for (var a = 0; a < TWO_PI; a += angle) { 
          var sx = cos(a) * this.rad; 
          var sy = sin(a) * this.rad; 
          vertex(sx, sy);
        } 
        endShape(CLOSE);
      };
    }
    
    var radiuses = [ 515, 313, 203, 142, 108, 79, 70, 60, 58 ];
    var rotations = [ 10, 9, 8, 7, 6, 5, 4, 3, 2 ];
    var pents = [];
    
    function setup() { 
      createCanvas(windowWidth, windowHeight);
      for( var i = 0; i < 9; i++ ) pents.push( new Pent(constrain(230-30*i,0,256), radiuses[i], rotations[i] ) ); 
      smooth();
    }
    
    function draw() { 
      background(255); 
      translate(windowWidth/2, windowHeight/2); 
      for( var i = 0; i < 9; i++ ) pents[i].draw();
    }
    

    Hey, that's nice. Slow, spinning pentagons. Now, if you don't care a lot about the exact values you were using, this is, IMHO, cleaner:

    function Pent( stroke_val, radius, max_rotation_speed ) {
      this.stk = stroke_val;
      this.rad = radius;
      this.ro = 0;
      this.dro = max_rotation_speed;
      this.draw = function() {
        stroke( this.stk );
        noFill();
        this.ro -= 1/2000 * this.dro  * ( 1 - dist(mouseX,mouseY,width/2,height/2)/dist(0,0,width/2,height/2) );
        var angle = TWO_PI / 5;
        rotate( this.ro );
        beginShape();
        for (var a = 0; a < TWO_PI; a += angle) { 
          var sx = cos(a) * this.rad; 
          var sy = sin(a) * this.rad; 
          vertex(sx, sy);
        } 
        endShape(CLOSE);
      };
    }
    
    var pents = [];
    
    function setup() { 
      createCanvas(windowWidth, windowHeight);
      for( var i = 0; i < 9; i++ ) pents.push( new Pent(230-25*i, 500-50*i, 10-i ) ); 
      smooth();
    }
    
    function draw() { 
      background(255); 
      translate(windowWidth/2, windowHeight/2); 
      for( var i = 0; i < 9; i++ ) pents[i].draw();
    }
    

    It even lines up nicely after a while. :-)

  • Hi! thank you, you saved me!

  • edited March 2018 Answer ✓

    Now take a minute and look at the difference. Really do. There's a bunch of good lessons here about how to write good code. It's shorter. You can add new pentagons by making a change in one place - the loop - without needing to re-write a bunch of lines of code. You can also make changes to the pentagons in one place - the class - without having to make changes to each of them. For example, maybe I want them filled in. Maybe I want stars. Maybe I want 7 points. Maybe I want nice colors. maybe I want the spinning to be able to go both ways. In fact, I do. Thus:

    function Pent( stroke_val, radius, max_rotation_speed ) {
      this.stk = stroke_val;
      this.rad = radius;
      this.ro = 0;
      this.dro = max_rotation_speed;
      this.draw = function() {
        stroke( 128 );
        fill( this.stk );
        this.ro += 1/2000 * this.dro  * ( map(mouseX,0,width,-1,1) );
        var angle = TWO_PI / 7;
        rotate( this.ro );
        beginShape();
        for (var a = 0; a <= 3*TWO_PI; a += 3*angle) { 
          var sx = cos(a) * this.rad; 
          var sy = sin(a) * this.rad; 
          vertex(sx, sy);
        } 
        endShape(CLOSE);
      };
    }
    
    var pents = [];
    
    function setup() { 
      createCanvas(windowWidth, windowHeight);
      colorMode(HSB, 255);
      for( var i = 0; i < 18; i++ ) pents.push( new Pent(color((120+10*i)%255,10*i,255), 500-25*i, 10-i ) ); 
      smooth();
    }
    
    function draw() { 
      background(255); 
      translate(windowWidth/2, windowHeight/2); 
      for( var i = 0; i < pents.length; i++ ) pents[i].draw();
    }
    

    Look at how few changes I had to make to accomplish this! DRY DRY DRY DRY DRY.

    WET bad. DRY good. Like socks.

  • Oh, man, this is really great! thank you so much! I need to create three similar sketches - adjusting speed and some other things you just show me how to work with them. Thank you!!

Sign In or Register to comment.