Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to space correctly a string of text in p5.js?

I saw many times really cool examples of kinetic typography. In these examples every letter is a particle. The text is a particle system and it may be subject to various forces such gravity or even centrifugal force. These systems are made in Processing and in p5.js.

I am building an interactive screen for the web filled with text using p5.js, inspired by these example of kinetic typography. When the user moves the cursor on the text this start bounce all around the screen.

I translated the sketch from Processing to p5.js and I have noticed this problem related to the spacing of the text in the setup() function. In Processing the code looks like this and it function correctly.

I want to focus on this section of the Processing code:

 void setup() {
 size(640, 360);
 //load the font
 f = createFont("Arial", fontS, true);
 textFont(f);
 // Create the array the same size as the String
 springs = new Spring[message.length()]; 
 // Initialize Letters (Springs) at the correct x location
 int locx = 40;
 //initialize Letters (Springs) at the correct y location
 int locy = 100;
  for (int i = 0; i < message.length(); i++) {
  springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i)); 
  locx += textWidth(message.charAt(i));
   //boudaries of text just to make it a nice "go to head"
   if (locx >= 360) {
   locy+=60;
   locx = 40;
   }
  }
}

You can see the result in this image

As you can see the parameter

   springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));    
   locx += textWidth(message.charAt(i));

does its job, spacing the letters.

However when I translate this sketch in P5.js, I don't get the same nice spacing. This is the same section but is the p5.js code:

  function setup() {
  createCanvas(640, 360);
  noStroke();
  textAlign(LEFT);
  // Create the array the same size as the String
  springs = new Array(message.length);
  // Initialize Letters (Springs) at the correct x location
  var locx = 10;
  //initialize Letters (Springs) at the correct y location
  var locy = 120;
   for (var i = 0; i < message.length; i++) {
   springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
   locx += textWidth(message.charAt(i));
   //boudaries of text just to make it a nice "go to head"
    if(locx>= 390){
    locy+= 60;
    locx= 40;
    }
   }  
  }

The result is show

in this image

I am sure, in the p5js code, that there is a problem regarding this part:

springs[i] = new Spring(locx, locy, 40, springs, i, message.charAt(i));
locx += textWidth(message.charAt(i));

Because I tried to fix it multiplying the value of the locx as shown here:

springs[i] = new Spring(locx*5, locy, 40, springs, i, message.charAt(i));

I then got

this result

which can seems correct, but I'm sure it is not.

At this point I have no idea how to fix it, it must be something of p5.js I'm unaware of. Any help is greatly appreciate.

//Edit As suggested in the comment here you can find the Spring class written in p5.js:

    // Spring class
class Spring {
  constructor (_x, _y, _s, _others, _id, letter_) {
    // Screen values
    this.x_pos = this.tempxpos = _x;
    this.y_pos = this.tempypos = _y;
    this.size = _s;
    this.over = false;
    this.move = false;

    // Spring simulation constants
    this.mass =  8.0;       // Mass
    this.k = 0.2;    // Spring constant
    this.damp =0.98;       // Damping
    this.rest_posx = _x;  // Rest position X
    this.rest_posy = _y;  // Rest position Y

    // Spring simulation variables
    //float pos = 20.0; // Position
    this.velx = 0.0;   // X Velocity
    this.vely = 0.0;   // Y Velocity
    this.accel = 0;    // Acceleration
    this.force = 0;    // Force

    this.friends = _others;
    this.id = _id;

    this.letter = letter_;
    

  }
    update() {

      if (this.move) {
        this.rest_posy = mouseY;
        this.rest_posx = mouseX;
      }

      this.force = -this.k * (this.tempypos - this.rest_posy);  // f=-ky
      this.accel = this.force / this.mass;  // Set the acceleration, f=ma == a=f/m
      this.vely = this.damp * (this.vely + this.accel);         // Set the velocity
      this.tempypos = this.tempypos + this.vely;           // Updated position


      this.force = -this.k * (this.tempxpos - this.rest_posx);  // f=-ky
      this.accel = this.force / this.mass; // Set the acceleration, f=ma == a=f/m
      this.velx = this.damp * (this.velx + this.accel);  // Set the velocity
      this.tempxpos = this.tempxpos + this.velx; // Updated position


      if ((this.overEvent() || this.move) && !(this.otherOver()) ) {
        this.over = true;
      } else {
        this.over = false;
      }
    }

    // Test to see if mouse is over this spring
    overEvent() {
      let disX = this.x_pos - mouseX;
      let disY = this.y_pos - mouseY;
      let dis = createVector(disX, disY);
      if (dis.mag() < this.size / 2 ) {
        return true;
      } else {
        return false;
      }
    }

    // Make sure no other springs are active
    otherOver() {
      for (let i = 0; i < message.length; i++) {
        if (i != this.id) {
          if (this.friends[i].over == true) {
            this.velx = -this.velx;
            return true;
          }
        }
      }
      return false;
    }
    //the springs collides with the edges of the screen 
    box_collision() {
  if(this.tempxpos+this.size/2>width){
  this.tempxpos = width-this.size/2;
  this.velx =  -this.velx;
  } else if (this.tempxpos - this.size/2 < 0){
  this.tempxpos = this.size/2;
  this.velx = -this.velx;
  }
  if (this.tempypos+this.size/2>height) {
  this.tempypos = height-this.size/2;
  this.vely = -this.vely;
  } else if (this.tempypos- this.size/2 < 0) {
  this.tempypos = this.size/2;
  this.vely = -this.vely;
  }
}
    //the springs collides with each other
    collide() {
      for (var i = this.id + 1; i < message.length; i++) {

        var dx = this.friends[i].tempxpos - this.tempxpos;
        var dy = this.friends[i].tempypos - this.tempypos;
        var distance = sqrt(dx*dx + dy*dy);
        var minDist = this.friends[i].size/2 + this.size/2;

        if (distance < minDist) { 
          var angle = atan2(dy, dx);
          var targetX = this.tempxpos + cos(angle) * minDist;
          var targetY = this.tempypos + sin(angle) * minDist;
          var ax = (targetX - this.friends[i].tempxpos) * 0.01;
          var ay = (targetY - this.friends[i].tempypos) * 0.01;
          this.velx -= ax;
          this.vely -= ay;
          this.friends[i].velx += ax;
          this.friends[i].vely += ay;
        }
      }
    }
  //display the letter Particle
    display() {
      if (this.over) {
        fill(255, 0, 0);
      } else {
        fill(255);
      }
      noStroke();
      textSize(fontS);
      //for debugging
      // ellipse(this.tempxpos, this.tempypos, this.size, this.size);
      text(this.letter, this.tempxpos, this.tempypos);
    }

    pressed() {
      if (this.over) {
        this.move = true;
      } else {
        this.move = false;
      }
    }

    released() {
      this.move = false;
      this.rest_posx = this.x_pos;
      this.rest_posy = this.y_pos;
    }
} 

And here you can find the link to the P5.js editor with the code: Spring text p5js code

note: I had to fix the Spring class because I didn't realize that all of my functions where initialized in the constructor. Now the functions that compose the Class are outside the constructor. I still haven't figured out how to fix the spacing problem.

like image 399
Luca Milanesio Avatar asked Dec 05 '20 23:12

Luca Milanesio


People also ask

What is float in p5 JS?

Converts a string to its floating point representation. The contents of a string must resemble a number, or NaN (not a number) will be returned. For example, float("1234.56") evaluates to 1234.56, but float("giraffe") will return NaN.

How to set the alignment of text in P5?

What's New ? The textAlign () function in p5.js is used to set the alignment for the drawing the text. This function accepts two parameter horizAlign and vertAlign. The horizAlign parameter sets the alignment on x-axis and the vertAlign parameter sets the alignment on y-axis.

Can I display text on screen with processing in P5?

The tutorial was ported to p5 by Arihant Parsoya. If you see any errors or have comments, open an issue on either the p5 or Processing repositories. If you are looking to display text onscreen with Processing, you’ve got to first become familiar with the String class.

What does the space character mean in the string separator parameter?

The space character (” “) is specified in this parameter to separate the string whenever a space occurs. The join () method is used to join an array of strings using a separator.

How do I remove space from a string in JavaScript?

The first parameter is given a regular expression with a space character (” “) along with the global property. This will select every occurrence of space in the string and it can then be removed by using an empty string in the second parameter.


1 Answers

I managed to resolve the issue.

As far as I understood the code was correct from the beginning.

What I missed through the journey was that I have to set the font size in the function setup() if I want to display the text on the screen correctly.

enter image description here

You can still see the result by checking the link to the p5.js editor I posted previously.

like image 96
Luca Milanesio Avatar answered Oct 06 '22 01:10

Luca Milanesio