Why my letters don't keep their shapes?!

Hello,
I managed to deform some letters and to scale them reacting to a sound file using Geomerative and minim libraries, but how can I keep the letters deformed?! Why once the sound position is 100px away the points get back to their original positions?!
Thanks a lot in advance for your help.
Best,
L

import processing.pdf.*;
import geomerative.*;
import ddf.minim.analysis.*;
import ddf.minim.*;

String soundNameFR = "FR_01";
Minim minim;
AudioPlayer soundsFR;
FFT fftFR;
float bandHeightFR;

int x, y;
String [] message={"music is like a mountain path"};
color textColor=0;
RFont f;
RShape gShape; 
float fontSize=110;
int splitGlyph = 120;
RPoint[][] pointsP;
float r = random(5, 20);
WordAttractor attractorW;
int tChildCount;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  size(1920, 1080);
  RG.init(this);
  f = new RFont("FreeSans.ttf", int( fontSize), LEFT);

  minim=new Minim(this);
  soundsFR=minim.loadFile("FR_01.wav", 512);
  soundsFR.play();
  fftFR= new FFT(soundsFR.bufferSize(), soundsFR.sampleRate());

  pointsP = new RPoint[message.length][];
  for (int i =0; i<message.length; i++) {
    //We create a RGroup to which we apply the font
    RGroup myGroupFR = f.toGroup(message[i]); 
    // We set this groupe to a ploygons group
    myGroupFR = myGroupFR.toPolygonGroup();
    // We call all the points stored inside the group
    pointsP[i] = myGroupFR.getPoints();
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void draw() {

  background(0);
  smooth();
  soundFFTAnalyse();
  pushMatrix();

  for (int l=0; l<message.length; l++) {
    translate(200, height/2);
    pointsP= new RPoint [message[l].length()][splitGlyph];

    RShape [] gShape = new RShape [message.length];
    //get grouped lettershapes from RFont    
    gShape [l] = f.toShape(message[l]); 
    //get the PShape[] containing all the single letters as RShapes. Each letter = children
    RShape[]letterShapes = gShape[l].children;
    //Number of childrens --> number of letters
    tChildCount = gShape[l].children.length;
    //int tChildrenCount = gShape[1].children.length;
    for (int k = 0; k < tChildCount; k++) { 
      RShape tShape = letterShapes[k];

      float posX=map(soundsFR.position(), 200, soundsFR.length(), 200, width-200); 
      float posY=bandHeightFR/8;
      // Calculate distance between attractor position and the center of each character
      float d= dist(posX, 0, gShape[l].children[k].getCenter().x, 0);
      //Scale each character according to its distance with mouseX
      float sx= map(d, 0, 100, 3, 1);
      float r= map(d, 0, 100, 0, -TWO_PI);
      if (d<100) {
        gShape[l].children[k].scale(sx, gShape[l].children[k].getCenter());
        //gShape[l].children[k].rotate(r, gShape[l].children[k].getCenter());
      }
      for (int j=0; j<splitGlyph; j++) {
        float frac=(1.0/splitGlyph);  
        pointsP[k][j]=gShape[l].children[k].getPoint(j*frac);
        RPoint topLeft = tShape.getTopLeft();
        RPoint bottomRight = tShape.getBottomRight();
        
        attractorW= new WordAttractor(posX-250, -280+posY, pointsP[k][j]);
        attractorW.attract();

        stroke(255);
        pushMatrix(); 
        translate( pointsP[k][j].x, pointsP[k][j].y);
        beginShape();
        noFill();
        strokeWeight(1);
        float angle = TWO_PI/18;
        rotate(j/angle+mouseX/500*noise(pointsP[k][j].x));
        bezier(-5*(noise(5)), 5, -10*(noise(2)), 5, -5*noise(10), -5, 5, -5);
        endShape();
        popMatrix();
      }
    }
  }
  popMatrix();
}

void soundFFTAnalyse() {
  AudioPlayer fr = soundsFR;
  fftFR.forward(fr.mix);
  for (int i=0; i<fftFR.specSize(); i++) {
    float bandDBFR = 10*log(fftFR.getBand(i)/fftFR.timeSize());
    bandHeightFR = map(bandDBFR*4, 0, -220, 0, height);
    constrain(bandHeightFR, 0, 1000);
    noStroke();
    fill(255, 0, 0);
    ellipse(fr.position()/10, bandHeightFR/20, 5, 5);
  }
}

class WordAttractor {

  float force_radious = 200;
  float maxForce = 50;
  RPoint position;
  RPoint points;
  float pX;
  float pY;

  WordAttractor(float x, float y, RPoint p) {
    points =p;
    position = new RPoint(x, y);
  }

  void attract() {
 
        float d= points.dist(position);
        if (d < force_radious) {   
          RPoint desired = new RPoint(points);
          desired.sub(position);
          desired.normalize();
          desired.scale(map(d, 0, force_radious, maxForce, 0));
          points.add(desired);
     
    }
  }
}

yes funny,
your old code had

above code shows

  float force_radious = 200;

? is that a radius v.s diameter problem?

Hey @kll,
Thanks for answering! No I don’t think it’s a radious problem! It just corresponds to where the attractor repulse the letter points.
What I don’t understand is why when I repulse the letters points, do they go back to their orginal positions once the sound.position() is 100 px away from each letter?

 attractorW= new WordAttractor(posX-250, -280+posY, pointsP[k][j]);
        attractorW.attract();

Same question when I enlarge the letters they scale back to their original size too. Obviously I need a condition to keep the letters enlarged and deformed according to the sound analyze. But how to do it?!
I’m turning around for days! Thanks a lot for your help in advance. L

 if (d<100) {
        gShape[l].children[k].scale(sx, gShape[l].children[k].getCenter());
      }

yes, that are the 2 places,
and you make the condition

if ( d < 100 ) 
``
and inside  attract() 
if (d < force_radious) 

if you want that to match you need

float force_radious = 200;

as global ( not in the class )
and use it in both cases??
is that what you need?

Thank you @kll for replying!
I am sorry I see I am not clear at all!
In fact it doesn’t matter even

if ( d >0 ) 
if (d < force_radious) 

and

float force_radious = 2000;

The problem stays the same, once the
soundsFR.position()
is more than soundsFR.length(), then the letters take back their original shape, they are not scaled nor deformed anymore. How can I keep the letters with different sizes for each of them(according to the frequences of the sound file) and deformed by the attractor?! Thank you so much for your help!

when the song is finished? i would expect that,
but you could change

soundsFR.play();

to

soundsFR.loop();

Hey,
Yes of course if the sound never stop, the program also,
but it’ s really not what I expect…
I d’ like to materialize the voice ferquencies of the words deforming the forms of the letters.
In an other sketch I managed to deform the letters and keep them deformed


But since I’d like to give a different scale to each letter I’ve used RShape() method to split each word into letters and then turn the children() into a 2D array of points:

pointsP[k][j]=gShape[l].children[k].getPoint(j*frac);
The problem is that the sketch doesn’t keep the deformations… Why? How to solve this problem? Maybe I should write a class with RShape?! I hope you can help me with this issue. Thanks a lot

i not know of any

method
but yes, you would need to give each character a memory,
like with a class…

How to give a memory to each character?!
I certainly don’t understand the way that class stores memory of an event. Could you please give me a very simple example?!

better you put your project aside for a while
and start alone making a first class example,
no strings, no music, no mouse operation
how a about a
++ circle class,
what remembers posx, posy, diam
and can draw itself with stroke and fill
and make a array over it for N random positioned circles.

when that is running post it here and you can start thinking
what the requirements are for a
array of class of a character for this project.

but if the concept of class is too early for you, just
use arrays and functions.
– actually most of the classes are useless as making them costs add time,
and that is only acceptable if you use that class again
minimal one more time.
( and that was the basic idea behind it, re-usability )

Dear @kll,
Didn’t think I should restart from the begining but it looks as if I should !! I’ll do it asap, but now I’m at work… Tomorrow.
Thanks a lot for helping me. best, L

Dear @kll,
Please find below the little starting sketch with a bunch of circles moving around…
The concept of class is not totally unknown to me, neither the use of arrays, but I’m confused with Geomerative I guess…!? What should I do now with my Geomerative sketch?!!
Thanks a lot for spending some time explaining me again what I don’t understand/forgot. L

Circle [] circles= new Circle[100];

void setup() {
  size(1920, 1080);
  smooth();
  for (int i =0; i<100; i++) {
    circles[i] = new Circle(random(width), random(height));
  }
}

void draw() {
  background(255);
  for (int i=0; i<circles.length; i++) {
    circles[i].update();
    circles[i].display();
  }
}

class Circle{
  
  PVector location;
  PVector velocity;
  PVector acceleration;
  float radious;
  
  Circle(float x, float y){
   location= new PVector(x,y);
   radious=15;
   velocity= new PVector(0.01,0.01);
   acceleration= new PVector(random(-0.1,0.1), random(-0.01,0.01)); 
  }
  
  void update(){
    velocity.add(acceleration);
    location.add(velocity);
    acceleration.mult(0);
  }
  
  void display() {
    fill(127);
    strokeWeight(0.5);
    stroke(0);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0,0,radious, radious);
    popMatrix();
  }
  
  void borders() {
    if(location.x <-radious) location.x = width+radious;
    if(location.y <-radious) location.y = height+radious;
    if(location.x > width+radious) location.x = -radious;
    if(location.y > height+radious) location.y = -radious;
  } 
}

if you use

ellipse(0, 0, 2*radious, 2*radious);

and call

borders();

from inside update(){}
it does something. good


now you make a list what

  • properties
  • functions
    a planned array of class of characters would need

and how you would fill that array from a String?

Hey @kll,
Thanks for your answer, I will try asap. I’ve updated my sketch(below) with a ‘repulse’ function inside my class and here it works the way I wish : the distance between mouseX coordinates and the point less than 100px away is added to this point coordinates, it’s pushed away and when mouse is more than 100px away the points don’t get back to their original locations!! I’ll try with Geomerative asap ;)) Thanks a lot, you help me very much

Circle [] circles= new Circle[100];

void setup() {
  size(1920, 1080);
  smooth();
  for (int i =0; i<100; i++) {
    circles[i] = new Circle(random(width), random(height));
  }
}

void draw() {
  background(255);
  for (int i=0; i<circles.length; i++) {
    circles[i].update();
    circles[i].display();
  }
}

class Circle{
  
  PVector location;
  PVector velocity;
  PVector acceleration;
  float radious;
  float maxForce=5;
  float radiousForce=100;
  
  Circle(float x, float y){
   location= new PVector(x,y);
   radious=15;
   velocity= new PVector(0,0);
   acceleration= new PVector(random(-0.1,0.1), random(-0.01,0.01)); 
  }
  
  void update(){
    velocity.add(acceleration);
    location.add(velocity);
    acceleration.mult(0);
    
    borders();
    
    PVector mouse= new PVector(mouseX, mouseY);
    float d=location.dist(mouse);
    if(d<100){
      PVector desired= new PVector(location.x, location.y);
      desired.sub(mouse);
      desired.normalize();
      desired.mult(map(d,0,radiousForce,0,5));
      location.add(desired);
    }
  }
  
  void display() {
    fill(127);
    strokeWeight(0.5);
    stroke(0);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0,0,radious, radious);
    popMatrix();
  }
  
  void borders() {
    if(location.x <-radious) location.x = width+radious;
    if(location.y <-radious) location.y = height+radious;
    if(location.x > width+radious) location.x = -radious;
    if(location.y > height+radious) location.y = -radious;
  } 
}

Dear @kll,

Here is my little attempt, obviously it’s more simple with a circle class!
I hope I am not too far from the solution… But still not what I expect, the attractor deformation still
not remain, why?! Thanks for your help.
Best, L

import geomerative.*;
import ddf.minim.analysis.*;
import ddf.minim.*;

String soundNameFR = "FR_01";
Minim minim;
AudioPlayer soundsFR;
FFT fftFR;
float bandHeightFR;

int x, y;
String [] message={"music is like a mountain path"};
color textColor=0;
RFont f;
RShape [] gShape = new RShape[message.length];
float fontSize=110;
int splitGlyph = 120;
RPoint[][] pointsP;
float r = random(5, 20);
WordAttractor attractorW;
int tChildCount;
characterSpec characters;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  size(1920, 1080);
  RG.init(this);
  f = new RFont("SFNSText.ttf", int( fontSize), LEFT);

  minim=new Minim(this);
  soundsFR=minim.loadFile("FR_01.wav", 512);
  soundsFR.play();
  fftFR= new FFT(soundsFR.bufferSize(), soundsFR.sampleRate());
  for (int l =0; l<message.length; l++) {
   characters = new characterSpec( gShape[l], message[l]); 
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void draw() {

  background(0);
  smooth();
  soundFFTAnalyse();
  //pushMatrix();
  translate(200, height/2);  
  for (int l =0; l<message.length; l++) {    
    characters.update();  
    characters.display();
  }
 // popMatrix();
}


void soundFFTAnalyse() {
  AudioPlayer fr = soundsFR;
  fftFR.forward(fr.mix);
  for (int i=0; i<fftFR.specSize(); i++) {
    float bandDBFR = 10*log(fftFR.getBand(i)/fftFR.timeSize());
    bandHeightFR = map(bandDBFR*4, 0, -220, 0, height);
    constrain(bandHeightFR, 0, 1000);
    noStroke();
    fill(255, 0, 0);
    ellipse(fr.position()/10, bandHeightFR/20, 5, 5);
  }
}

class characterSpec {

  RShape bShape;
  String m;
  RShape  letterShapes;
  float maxForce=2;
  float radiousForce=100;

  characterSpec(RShape _letterShapes, String _m) {

    letterShapes=_letterShapes;
    m = _m;
    pointsP= new RPoint [m.length()][splitGlyph];
    bShape = new RShape();    
    bShape = f.toShape(m); 
    RShape [] letterShapes = bShape.children;
    letterShapes = bShape.children;
    tChildCount = bShape.children.length;
  }

  void update() {
    for (int k = 0; k < tChildCount; k++) {
      float posX=map(soundsFR.position(), 200, soundsFR.length(), 200, width-200); 
      float posY=bandHeightFR/4;
      float d= dist(posX, 0, bShape.children[k].getCenter().x, 0);
      float sx= map(d, 0, 100, 1.1, 1);
      sx=abs(sx);
      //float r= map(d, 0, 100, 0, -TWO_PI);
      if (d<100) {
        bShape.children[k].scale(sx, bShape.children[k].getCenter());
        //bShape.children[k].rotate(r, bShape.children[k].getCenter());
      }
      for (int j=0; j<splitGlyph; j++) {
        float frac=(1.0/splitGlyph);  
        pointsP[k][j]=bShape.children[k].getPoint(j*frac);
        attractorW= new WordAttractor(posX-200, posY-500, pointsP[k][j]);
        attractorW.attract();
      }
    }
  }

  void display() {
    for (int k = 0; k < tChildCount; k++) {
      for (int j=0; j<splitGlyph; j++) {
        pushMatrix();
        translate(pointsP[k][j].x, pointsP[k][j].y);
        stroke(255);
        beginShape();
        noFill();
        strokeWeight(0.2);
        float angle = TWO_PI/18;
        rotate(angle*j/4+noise(pointsP[k][j].x)/20);
        bezier(-5*(noise(5)), 5, -10*(noise(2)), 5, -5*noise(10), -5, 5, -5);
        endShape();
        popMatrix();
      }
    }
  }
}

class WordAttractor {

  float force_radious = 100;
  float maxForce = 50;
  RPoint position;
  RPoint points;
  float pX;
  float pY;

  WordAttractor(float x, float y, RPoint p) {
    points =p;
    position = new RPoint(x, y);
  }

  void attract() {

    float d= points.dist(position);
    if (d < force_radious) {   
      RPoint desired = new RPoint(points);
      desired.sub(position);
      desired.normalize();
      desired.scale(map(d, 0, force_radious, maxForce, 0));
      points.add(desired);
    }
  }
}

Dear @kll,
I hope you will find a bit of time to help me, cause I really need as you can see!
I am quite a newbie in programmation and not very confortable with all the concepts yet!
Oriented object programmation has still some mysteries I need to catch…
Thanks in advance for your help. Best, L

sorry, but from reading i not see/understand the problem.
and without files it can not be tested…

Hey kll, Thanks for your reply.I’ll write a version with mouseX/mouseY or better attach the sound file! In one hour and half. Thanks for your help…

Laurent Mareschal
https://laurentmareschal.com


P +33 (0)6.65.62.44.59

----- Mail d’origine -----

Dera @kll,

here is the code with variables using mouseX and mouseY…Thanks for your patience and help…

import geomerative.*;
import ddf.minim.analysis.*;
import ddf.minim.*;

String soundNameFR = "FR_01";
Minim minim;
AudioPlayer soundsFR;
FFT fftFR;
float bandHeightFR;

int x, y;
String [] message={"music is like a mountain path"};
color textColor=0;
RFont f;
RShape [] gShape = new RShape[message.length];
float fontSize=110;
int splitGlyph = 120;
RPoint[][] pointsP;
float r = random(5, 20);
WordAttractor attractorW;
int tChildCount;
characterSpec characters;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  size(1920, 1080);
  RG.init(this);
  f = new RFont("SFNSText.ttf", int( fontSize), LEFT);

  minim=new Minim(this);
  soundsFR=minim.loadFile("FR_01.wav", 512);
  soundsFR.play();
  fftFR= new FFT(soundsFR.bufferSize(), soundsFR.sampleRate());
  for (int l =0; l<message.length; l++) {
   characters = new characterSpec( gShape[l], message[l]); 
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void draw() {

  background(0);
  smooth();
  soundFFTAnalyse();
  //pushMatrix();
  translate(200, height/2);  
  for (int l =0; l<message.length; l++) {    
    characters.update();  
    characters.display();
  }
 // popMatrix();
}


void soundFFTAnalyse() {
  AudioPlayer fr = soundsFR;
  fftFR.forward(fr.mix);
  for (int i=0; i<fftFR.specSize(); i++) {
    float bandDBFR = 10*log(fftFR.getBand(i)/fftFR.timeSize());
    bandHeightFR = map(bandDBFR*4, 0, -220, 0, height);
    constrain(bandHeightFR, 0, 1000);
    noStroke();
    fill(255, 0, 0);
    ellipse(fr.position()/10, bandHeightFR/20, 5, 5);
  }
} 

class characterSpec {

  RShape bShape;
  String m;
  RShape  letterShapes;
  float maxForce=2;
  float radiousForce=100;

  characterSpec(RShape _letterShapes, String _m) {

    letterShapes=_letterShapes;
    m = _m;
    pointsP= new RPoint [m.length()][splitGlyph];
    bShape = new RShape();    
    bShape = f.toShape(m); 
    RShape [] letterShapes = bShape.children;
    letterShapes = bShape.children;
    tChildCount = bShape.children.length;
  }

  void update() {
    for (int k = 0; k < tChildCount; k++) {
      //float posX=map(soundsFR.position(), 200, soundsFR.length(), 200, width-200); 
      //float posY=bandHeightFR/4;
      float posX = mouseX; 
      float posY = mouseY;
      float d= dist(posX, 0, bShape.children[k].getCenter().x, 0);
      float sx= map(d, 0, 100, 1, 1.01);
      //sx=abs(sx);
      //float r= map(d, 0, 100, 0, -TWO_PI);
      if (d<100) {
        bShape.children[k].scale(sx, bShape.children[k].getCenter());
        //bShape.children[k].rotate(r, bShape.children[k].getCenter());
      }
      for (int j=0; j<splitGlyph; j++) {
        float frac=(1.0/splitGlyph);  
        pointsP[k][j]=bShape.children[k].getPoint(j*frac);
        attractorW= new WordAttractor(posX-200, posY-500, pointsP[k][j]);
        attractorW.attract();
      }
    }
  }

  void display() {
    for (int k = 0; k < tChildCount; k++) {
      for (int j=0; j<splitGlyph; j++) {
        pushMatrix();
        translate(pointsP[k][j].x, pointsP[k][j].y);
        stroke(255);
        beginShape();
        noFill();
        strokeWeight(0.2);
        float angle = TWO_PI/18;
        rotate(angle*j/4+noise(pointsP[k][j].x)/20);
        bezier(-5*(noise(5)), 5, -10*(noise(2)), 5, -5*noise(10), -5, 5, -5);
        endShape();
        popMatrix();
      }
    }
  }
}

class WordAttractor {

  float force_radious = 100;
  float maxForce = 50;
  RPoint position;
  RPoint points;
  float pX;
  float pY;

  WordAttractor(float x, float y, RPoint p) {
    points =p;
    position = new RPoint(x, y);
  }

  void attract() {

    float d= points.dist(position);
    if (d < force_radious) {   
      RPoint desired = new RPoint(points);
      desired.sub(position);
      desired.normalize();
      desired.scale(map(d, 0, force_radious, maxForce, 0));
      points.add(desired);
    }
  }
}

oh, i was thinking that is a MCVE ( Minimal, Complete, and Verifiable Example)
but still need font - and sound file.
and also not understand your code ( possibly above my level )
i expected that the class contains one character and its position …

but ok, i never play geomerative , so pls wait for someone who
has more knowledge about this. kll out.