Who knows well Geomerative?!

Well, in the sketch you sent me it does only the last line :

just make it part of the exhibition

you can also comment out the update:

    if (sentenceCurrentlyModified==i) { 
      // characters[i].update();
    }

because what happens in update also happens in display, in these line in draw to be specific:

    if (sentenceCurrentlyModified>=i) {
      characters[i].display((sentenceCurrentlyModified == i));
    }

Hey Chrisir,

Thank you very much for your answers. Maybe it will be clearer with a picture.

Here is how the text is supposed to look at the end of the sketch. Each line is deformed by a different sound

(sound 1 deforms line 1, sound 2 deforms line 2 and so on) by the attractor, and each letter is scaled according to the sound too, like in this picture (see attached ADM_03.png). Plus the long bezier curves that move along with the sound too but don’t stay on screen.

-The 1rst problem I see in the sketch I’ve sent you is that before the text is displayed it is already deformed (some letters are squeezed and twisted) why ? Since we select one sentence and apply an attractor on this last one only?! It’s like if the attractor attracts every line on its 1rst occurence…

The 2nd problem I see is that the letters are enlarged in the first lines but after 4 lines it looks like the scale() function in the Character class is not effective. Should I give the attractorPosXFR the positions of each sentence?

Since they all start at different X coordinates?!

Right now it doesn’t correspond to what I’d like to show! I wanted the words to take the shape of the voice

that it will be almost like an heart beat diagram!

I hope it is clearer now? I am so sorry I wish it would correspond now to what I would like to…And to avoid you some extra work.

I hope you can help me with these issues. Thanks a lot @Chrisir.

Best,

L

Laurent Mareschal
https://laurentmareschal.com


P +33 (0)6.65.62.44.59

I am sorry

I don’t have time now

No worries, whenever you will have time today, tomorrow, on thursday… I work today so no time to makes new tests anyway. Thanks, L

Try this please

Also, do you want the change to be permanent when the current line is finished?

Honestly I don’t think I have time the rest of the week

Hi @Chrisir,
Thanks for your message.
No worries, I’ll work on it at the end of this week anyway so I’ll keep you posted

and maybe then you will have a bit of time?!

Laurent Mareschal
https://laurentmareschal.com


P +33 (0)6.65.62.44.59

Dear @Chrisir,
At the end I’ve found some mistakes in my code and correct them, but the last debug came from a friend of mine with special functions of the Geomerative library inside my Characters’ class.
Thank you anyway for your help.
I put the code here below in case soeone else will be stuck with a similar problem:

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

//TEXT
int indexPhraseSetFR;
int indexPhraseFR;
int x, y, i, j;
String [][]message={
  {"On me dit de te haïr et je m'y efforce"}, 
  {"Je t'imagine cruel, violent, implacable"}, 
  {"mais à te voir je n'ai bientôt plus de force"}, 
  {"et de te blesser je suis bien incapable"}, 

  {"Tous mes camarades combattent avec rage"}, 
  {"Et pleurent la nuit au souvenir des supplices"}, 
  {"Infligés à leurs frères qui sont du même âge"}, 
  {"et rêvent comme eux de toucher une peau lisse"}, 

  {"Et de pouvoir enfin caresser des obus"}, 
  {"Autres que ceux produits par le pouvoir obtus"}, 
  {"Je rêve de quitter ces boyaux infernaux"}, 

  {"De laisser ces furieux des deux bords du Rhin"}, 
  {"Et de pouvoir embrasser ta chute de rein"}, 
  {"Et porter notre amour sur les fonts baptismaux"}
};

color textColor=0;
RFont f;
float fontSize=60;
int splitGlyph = 120;
//RPoint[][] pointsP;
float r = random(5, 20);
WordAttractor attractorW;

RShape [][] gShape = new RShape[message.length][message.length]; 
characterSpec [][] characters= new characterSpec[message.length][message.length];
int sentenceCurrentlyModified;
boolean modifyThis;
float bottomLeftPointsXFR;
float bottomRightPointsXFR;

//SOUND
SoundManager sm;
int startTime;
int loop=0;
float bandHeightFR;
boolean isTransitioning = false;
boolean messageOn = false;
String []soundNamesFR = {"FR_01", "FR_02", "FR_03", "FR_04", "FR_05", "FR_06", "FR_07", "FR_08", "FR_09", "FR_10", "FR_11", "FR_12", "FR_13", "FR_14"};

float transitionTime = 0;
int startTransitionTime;
int transitionDuration = 1500;
float val;

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

void setup() {
  size(1600, 1200);
  //fullScreen(SPAN);
  RG.init(this);
  f = new RFont("SFNSText.ttf", int( fontSize), LEFT);

  //  listWordAttractors = new WordAttractor [message.length][splitGlyph];
  //  listDistMap = new float[message.length][splitGlyph];
  //  listPV = new PVector[message.length][splitGlyph]  ;
  // pointsP = new RPoint[message.length][splitGlyph];

  for (int i=0; i<message.length; i++) {
    for (int j=0; j<message[i].length; j++) {
      characters[i][j]= new characterSpec( i, message[i][j]);
    }
  }

  //SOUND
  sm= new SoundManager(this);
  indexPhraseFR = 0;
  indexPhraseSetFR = -1;
  // TIME
  startTime=millis();
}

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

void draw() {

  background(0);
  smooth();
  scale(0.7);
  translate(1900, 0);
  rotate(1.58);
  sm.update();
  
  translate(350, -50);

  for (int i=0; i<message.length; i++) {
    for (int j=0; j<message[i].length; j++) {
      // println("i"+":"+i);

      translate(0, i+1*80);
      if (i%4==0) {
        translate(-80, 0);
      }
      if (i<3) {
        translate(i*20, 0);
      } else if (i==3) {
        translate(130, 0);
      } else if (i>3 && i<7) {
        translate(-280, 0);
        translate(i*45, 0);
      } else if (i==7) {
        translate(-150, 0);
      } else if (i==8) {
        translate(355, 0);
      } else if (i==9) {
        translate(-150, 0);
      } else if (i==10) {
        translate(200, 0);
      } else if (i>10 && i <12) {  
        translate(-160, 0);
      } else if (i==12) {
        translate(160, 0);
      } else if (i==13) {
        translate(-190, 0);
      }
      if (i<=8 && i%4==0) {
        translate(0, 60);
      }
      if (  i==11 ) {
        translate(0, 60);
      }

      if (sentenceCurrentlyModified==i && j==0) { 
        characters[i][j].update();
      } 

      if (sentenceCurrentlyModified==i && j==0) {
        characters[i][j].display((sentenceCurrentlyModified == i));
      } else if (sentenceCurrentlyModified>=i && j==0) {
        characters[i][j].display((sentenceCurrentlyModified == i));
      }
      if (millis() >= startTransitionTime-100 && millis() <= startTransitionTime+transitionDuration){
        fade();
      }
    }
  }
}
void fade() {
  //if (millis() >= startTransitionTime && millis() <= startTransitionTime+transitionDuration) {
    pushMatrix();
    translate(-150, -1500);
    noStroke();
    val = map(millis(), startTransitionTime, startTransitionTime+transitionDuration, 0., 255.);
    fill(0, val);
    rect(0, 0, 2200, 1800);
    popMatrix();
  //}
}

class characterSpec {
  String  m;
  int tChildCount;
  RShape bShape;
  RShape cShape;
  RShape[] letterShapes;
  int indexPhrase;
  // We create 3 new lists parallel to pointsP
  RPoint[][] pointsP;
  // List of all the distances between all the attractors and the pointsP
  float [][] listDistMap;
  // List of vectors to replace the pointsP coordinates
  PVector[][] listPV;
  WordAttractor[][] listWordAttractors;
  
  characterSpec(int index, String _m) {
    println("index : "+index  + " phrase: " + _m);
    indexPhrase = index;
    m=_m;

    listWordAttractors= new WordAttractor [m.length()][splitGlyph];
    listDistMap = new float [m.length()][splitGlyph];
    for (int n=0; n <m.length(); n++) {
      for (int j=0; j<splitGlyph; j++) {
        listDistMap[n][j]=100000;
      }
    }
    listPV = new PVector[m.length()][splitGlyph];

    if (m.length()>0) {
      bShape= new RShape();
      bShape= f.toShape(m);
      letterShapes = bShape.children;
      tChildCount = bShape.children.length;
    }

    pointsP= new RPoint [m.length()][splitGlyph];
    for (int k=0; k<tChildCount; k++) {  
      for (int j=0; j<splitGlyph; j++) { 
        float frac =(1.0/splitGlyph);
        pointsP[k][j]=bShape.children[k].getPoint(j*frac);
      }
    }
  }

  void update() {

    float frac =(1.0/splitGlyph);
    for (int k=0; k<tChildCount; k++) {  
      RShape tempShape = new RShape();
      tempShape.addMoveTo(pointsP[k][0]);
      for (int j =1; j<splitGlyph; j++) {
        tempShape.addLineTo(pointsP[k][j]);
        tempShape.getBottomLeft();
        tempShape.getBottomRight();
        bottomLeftPointsXFR= tempShape.getBottomLeft().x;
        bottomRightPointsXFR= tempShape.getBottomRight().x;
      }
      tempShape.addClose();

      float d= dist(attractorPosXFR,attractorPosYFR, tempShape.getCenter().x,  tempShape.getCenter().y);

      if (d<75) {
        float s=map(d, 0, 75, 1.0, 1.45);
        tempShape.scale(s, bShape.children[k].getCenter());
      }

      for (int j=0; j<splitGlyph; j++) { 
        pointsP[k][j] = tempShape.getPoint(j*frac);
      }
      WordAttractor wa =new WordAttractor(attractorPosXFR, attractorPosYFR, pointsP[k]);
      wa.attract();
    }
  }


  void display(boolean modifyThis) {
    for (int k=0; k<tChildCount; k++) {  
      for (int j=0; j<splitGlyph; j++) { 
        pushMatrix();
        translate(pointsP[k][j].x, pointsP[k][j].y);
        beginShape();
        stroke(255);
        noFill();
        strokeWeight(0.5);
        float angle = TWO_PI/18;
        rotate(angle*j/4+noise(pointsP[k][j].x)/20);
        bezier(-3*(noise(5)), 3, -6*(noise(2)), 3, -4*noise(10), -3, 3, -3);
        endShape();
        popMatrix();
        if (modifyThis) {
          //listWordAttractors[k][j].attract();
          if (k<=tChildCount) {
            pushMatrix();
            translate(pointsP[k][j].x, pointsP[k][j].y);
            RPoint attractorPosFR = new RPoint(attractorPosXFR, attractorPosYFR);
            attractorPosFR = new RPoint(attractorPosXFR, attractorPosYFR);
            float dist = pointsP[k][j].dist(attractorPosFR);
            if (dist<150) {
              dist = map(dist, 150, 0, 1, 0);
              if (attractorPosFR.x ==0) {
                stroke(255, 10) ;
              } else { 
                stroke(255, dist*255);
              }
              float angle_01 = TWO_PI*10;
              //rotate (j/angle*frameCount/10);
              rotate (j/angle_01*2);
              if (modifyThis) {
                bezier(-2*(noise(10)), 10, 10*(noise(10)), -5, 2*noise(5), -10, 10, dist*attractorPosFR.x/15);//
              } else {
                bezier(-2*(noise(10)), 10, 10*(noise(10)), -5, 2*noise(5), -10, 10, 1);//
              }
            }
            // print(".");
            popMatrix();
            // to check out where is the attractor
            noStroke();
            fill(255, 0, 0);
            ellipse(attractorPosXFR, attractorPosYFR, 20, 20);
          }
        }
      }
    }
  }
}


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


//variable 'public'

float attractorPosXFR, attractorPosYFR;
float []linesYPositions1 ={80., 100., 120., 130., 0., 10., 20., 30., 80., 90., 100., 110., 120., 130};
float []linesXPositions ={100., 120., 140., 160., 0., -20., -40., -60., 80., 100., 120., 20., 40., 60.};


class SoundManager {
  //SOUND
  Minim minim;
  AudioPlayer background;
  AudioPlayer[]soundsFR;
  FFT fftFR;

  SoundManager(PApplet app) {

    minim = new Minim(app);
    background = minim.loadFile("WAR.wav");
    soundsFR = new AudioPlayer[soundNamesFR.length];
    for (int i =0; i<soundNamesFR.length; i++) {
      soundsFR[i] = minim.loadFile(soundNamesFR[i]+".wav", 512);
    }
  }

  void update() {

    if (isTransitioning) {
      if (millis() >= startTransitionTime+transitionDuration) {
        // transition end
        // si c'est pas fini on passe au suivant
        if (indexPhraseSetFR < soundsFR.length) {
          isTransitioning=false;
          transitionTime=0; 
          //nextPhraseSetFR();
          playSound();
          // si on a lu tous les groupes on recommence à zero
        } else if (indexPhraseSetFR == soundsFR.length && millis() >= startTransitionTime+transitionDuration) {

          // reset from the beginning 
          isTransitioning=false;
          transitionTime=0;    
          indexPhraseSetFR=0;
          indexPhraseFR=0;
          //nextPhraseSetFR();
          sentenceCurrentlyModified=0;
          background.shiftGain(-20, -80, 1000);
          background.rewind();
          background.pause();
          startTime = millis();
          playSound();
          background.play();
          background.shiftGain(-80, -20, 1000);
        }
      } else if (millis() <= startTransitionTime+transitionDuration) {       
        //  we are transiting 
        
        transitionTime = map(millis(), startTransitionTime, startTransitionTime+transitionDuration, 0., 1.);
        for (int i=0; i<message.length; i++) {
          for (int j=0; j<message[i].length; j++) {
            characters[i][j]= new characterSpec( sentenceCurrentlyModified, message[i][j]);
          }
        }
      }
    } else {
      if ( indexPhraseSetFR==-1) { 
        // Initialise all 
        sentenceCurrentlyModified=0;
        indexPhraseSetFR=0;
        indexPhraseFR=0;
        playSound();
        background.play();
        background.shiftGain(-80, -20, 1000);
      } else if ( !soundsFR[indexPhraseFR].isPlaying()) {  
        // sound file is finished read next one
        indexPhraseFR++;
        sentenceCurrentlyModified++;
        //sentenceCurrentlyModified=indexPhraseFR;
        delay(1500);
        if ( isTransitioning==false && indexPhraseFR >= soundsFR.length) {
          // If phrases'index is greater than the stanza's index then go on to the next stanza
          delay(3000);
          indexPhraseFR=0;// 1rst sentence
          sentenceCurrentlyModified=0;
          indexPhraseSetFR++;// increase stanza's index
          isTransitioning = true;

          startTransitionTime = millis();
        } else {
          //go to the next phrase   
          //nextPhrase();
        }
        if (millis() >= startTransitionTime+transitionDuration) {
          playSound();
        }
      } else { 
        // we're reading the sound file
        // analyse of the sound
        soundFFTAnalyse();
        wordAttractorToSound();
      }
    }
  }

  void pauseSound() {
    AudioPlayer fr = soundsFR[indexPhraseFR];
    fr.rewind();
    fr.pause();
    fftFR = new FFT(fr.bufferSize(), fr.sampleRate());
  }

  void playSound() {
    AudioPlayer fr = soundsFR[indexPhraseFR];
    fr.rewind(); 
    fr.play();
    fftFR = new FFT(fr.bufferSize(), fr.sampleRate());
    attractorPosXFR = 0;
    attractorPosYFR =indexPhraseFR*50;
    bandHeightFR = 0;
  }

  void soundFFTAnalyse() {
    AudioPlayer fr = soundsFR[indexPhraseFR];
    fftFR.forward(fr.mix);
    for (int i =0; i< fftFR.specSize(); i++) {
      float bandDBFR = 10*log(fftFR.getBand(i)/fftFR.timeSize());
      bandDBFR = constrain(bandDBFR, -1000, 1000);
      bandHeightFR = map(bandDBFR*4, 0, -220, 0, height);
    }
  }

  void wordAttractorToSound() {
    AudioPlayer fr = soundsFR[indexPhraseFR]; 
    attractorPosXFR = map(fr.position(), 0, fr.length(), bottomLeftPointsXFR-width/1.25, bottomRightPointsXFR+width/16);

    if (indexPhraseFR <=3) {
      attractorPosYFR = bandHeightFR/10-290-5*indexPhraseFR;
    }
    if (indexPhraseFR >3 && indexPhraseFR <=7) {

      attractorPosYFR = bandHeightFR/10-270-5*indexPhraseFR;
    }
    if (indexPhraseFR >7 && indexPhraseFR <=10) {
      attractorPosYFR = bandHeightFR/10-280+5*indexPhraseFR;
    }
    if (indexPhraseFR >10 && indexPhraseFR <=13) {
      attractorPosYFR = bandHeightFR/10-290+5*indexPhraseFR;
    }
  }
}

class WordAttractor {

  float force_radious =200;
  float maxForce = 12;
  RPoint position;
  RPoint[] points;
  float pX;
  float pY;

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

  void attract() {
for (int i=0; i<points.length; i++){
      float d= points[i].dist(position);
      if (d < force_radious) {   
        RPoint desired = new RPoint(points[i]);
        desired.sub(position);
        desired.normalize();
        desired.scale(map(d, 0, force_radious, maxForce, 0));
        points[i].add(desired); 
      }
    }
  }
  void display () {
    translate( width/2,0);
    stroke(255,0,0);
    strokeWeight(10);
    ellipse (position.x, position.y, 30, 30);
  }
  void moveTo(float x, float y) {
    position.x=x;
    position.y=y;
  }
}
1 Like

my apologies I couldn’t help you!!!

I was really busy.

Glad you had help.

Please consider post your code with all the stuff on github since here we can’t run it.

Also the code is much nicer with multiple tabs.

Thank you and my apologies again.

Chrisir

Dear @Chrisir,
No problem, I perfectly understand you were very busy and that you couldn’t help…
I can put the sketch and the whole stuff on Github, or send you the whole package through
we transfer. I guess if someone wants to transform dynamically the size, rotate each letter of a word/sentence independently using the Geomerative Library, this last code will be helpfull even without
the sound files…
All the best,
Laurent

1 Like