Why my letters don't keep their shapes?!

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.

Dear @kll,
this is almost a MCVE since even if I haven’t got rid of the sound it doesn’t have any effect here just mouseX and mouseY are used as variables. Regarding the font, any font in .ttf format can be used as RFont()… We need this to use Geomerative library. And for the library, not sure you need to know so much, I just think I misplaced my attractor in the sketch and also where I change the letter size… It’s more a general programmation issue I think. Thank you very much for your help. Best wishes, L

Dear @kll,

I’m coming back to you about my problem regarding my deformed typography, remember?!
Well the solution was not linked to the Geomerative library. @Chrisir gave me the solution, so I shere it with you ! Thank you very much again for your help. I appreciate. see you around. best, L
Here is the comment by @Chrisir to explain the code below:

Blockquote The basic idea is simple: When we want the letters to keep their position after the mouse is gone, we need to store the mouse position and tell the WordAttractor to use the stored position.

But the storing only works when we don’t store the mouse position throughout but only when the mouse is close to the letter. Hence I check for the min distance and store the mouse position then when the distance is minimal (or when key is als pressed).

I am using these 2D arrays for this:

RPoint[][] pointsP;
WordAttractor[][] listWordAttractors;  
float[][] listDistMap; 
PVector[][] listPV;

The pointsP was there already

Blockquote

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;
WordAttractor[][] listWordAttractors;  
float[][] listDistMap; 
PVector[][] listPV;

float r = random(5, 20);
WordAttractor attractorW;
int tChildCount;
characterSpec characters;

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

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

  minim=new Minim(this);
  soundsFR=minim.loadFile("jingle.mp3", 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];

    listWordAttractors = new WordAttractor [m.length()][splitGlyph]; //
    listDistMap = new float  [m.length()][splitGlyph]; //
    for (int k = 0; k < m.length(); k++) {
      for (int j=0; j<splitGlyph; j++) {
        listDistMap[k][j]=1000000;
      }
    }
    listPV = new PVector  [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);

        float dist1 = dist(posX-200, posY-500, 
          pointsP[k][j].x, pointsP[k][j].y);

        // if (dist1 < listDistMap[k][j]) {
        if (keyPressed&&dist1 < 200) {
          listDistMap[k][j]=dist1; 
          listPV[k][j] = new PVector(posX-200, posY-500); 
          listWordAttractors[k][j] = new WordAttractor(listPV[k][j].x, listPV[k][j].y, pointsP[k][j]);
        }

        if (pointsP[k][j]!=null&&listWordAttractors[k][j]!=null) {
          listWordAttractors[k][j].points = pointsP[k][j];
          listWordAttractors[k][j].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);
    }
  }
}
//
3 Likes