G4P Sliders dance in Class

Hello community :man_technologist:
I want to increase the slider movements in variable steps und schau/see also this increments. For example from start-position 0 to end-position 60, the slider should move in one steps: 0, 0+1, 2…59, 60. The is what i want to achieve:

  • The movement of the sliders should be updated after every step (show/see the increments)
  • The sliders should move simultaneously
    Well im new in Processing and i have basic programming knowledge, but please don t be afraid to suggest advanced solutions so that i can learn more :-).

With a for loop i made the sliders move in +1 steps but i can NOT make them move simultaneously. that s why i tried to solve this with a Class. But now nothing work.This is the part of the code with the described probleb:

import g4p_controls.*;

SdrDance Dance1;
SdrDance Dance2;

GCustomSlider sdr1,sdr2;
GButton Dance;

int StartPos, EndPos, EndPos1, EndPos2, speed1, speed2; 

void setup(){ 
  size(400,200);
  GUI();
}

void draw(){
  background (180); 
}

public void Dance_click1(GButton source, GEvent event) {
    speed1 = 1;
    speed2 = 2;
    EndPos1 = 60;
    EndPos2 = 60;
   
    Dance1 = new SdrDance(sdr1.getValueI(),EndPos1, speed1);
    Dance2 = new SdrDance(sdr2.getValueI(),EndPos2, speed2);
    
    Dance1.move(); // i can t see the increment of the slider --> just jumping from startposition to the endposition
    Dance2.move(); // Slider should move also simultaneously --> is it not possible to check it at the moment becouse no increment
} 

//*****Class
class SdrDance {  
  int StartPos;
   int EndPos;
   int speed;
   
  SdrDance (int x_, int y_, int z_) {
    StartPos= x_;
    EndPos =y_;
    speed = z_;
  } 
  
  void move(){   
     while (StartPos <= EndPos) { 
        sdr1.setValue(StartPos);
        sdr2.setValue(StartPos);
        println(StartPos);
        StartPos = StartPos + speed;
               
     }
    while (StartPos >= EndPos) { 
        sdr1.setValue(StartPos);
        sdr2.setValue(StartPos);
        println(StartPos);
        StartPos = StartPos - speed;            
     }
   }  
}

//******GUI
void GUI(){
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(5, 0, 180);
  sdr1.addEventHandler(this, "sdr1_change1");
  
  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(5, 0, 180);
  sdr2.addEventHandler(this, "sdr2_change1");
  
  Dance = new GButton(this, 175,15, 55, 25);
  Dance.setText("Dance");
  Dance.addEventHandler(this, "Dance_click1");
}

public void sdr1_change1(GCustomSlider source, GEvent event) {
} 

public void sdr2_change1(GCustomSlider source, GEvent event) {
} 

public void handleSliderEvents(GValueControl slider, GEvent event) {
}

as you can see, the sliders just jump from start position to the end and the don t move simultaneously. How wold you solve this? is there already an example in the forum?
Thank you so much for any help :relaxed:

1 Like

I have no idea why you might want to do this because G4P are input controls but never mind. :smile:

The main problem is the move() method because it moves the slider from startPos to endPos in one frame so you only ever get to see it in the final position. There are several solutions that don’t require a class but there is no harm in creating one and in fact it provides a fairly neat solution once you get the code right.

I have modified your code and provided a solution below but with various changes which might seem pedantic but I will explain now.

  • By convention in Java all method and variable identifiers start with an optional underscore followed by a lower case letter e.g. dance, _speed
  • By convention in Java all class names start with an uppercase letter e.g. SdrDance
  • Variable and parameter identifiers should have meaning so using x_ for startPos and z_ for speed is bad programming practice.
  • A user defined class should be autonomous. That means the only data it should access are the class attributes / fields or data passed as method parameters. So I have added a slider attribute.

There are many Java source code conventions and it is good practice to learn them because it makes it easier for other programmers make sense of your code. :wink:

OK so here is my solution, as I said there are many ways this could be coded but it depends on how the overall purpose of the sketch. I also wanted to make the minimum number of changes to your code.

import g4p_controls.*;

SdrDance dance1;
SdrDance dance2;

GCustomSlider sdr1, sdr2;
GButton btnDance;

int startPos, endPos, endPos1, endPos2, speed1, speed2; 

void setup() { 
  size(400, 200);
  createGUI();
}

void draw() {
  background (180);
  if (dance1 != null && !dance1.finished()) {
    dance1.update();
  }
  if (dance2 != null && !dance2.finished()) {
    dance2.update();
  }
}

public void btnDance_click1(GButton source, GEvent event) {
  speed1 = 1;
  speed2 = 1;
  endPos1 = 60;
  endPos2 = 60;
  dance1 = new SdrDance(sdr1, sdr1.getValueI(), endPos1, speed1);
  dance2 = new SdrDance(sdr2, sdr2.getValueI(), endPos2, speed2);
} 

//*****Class
class SdrDance {  
  GCustomSlider slider;
  int startPos;
  int endPos;
  int speed;
  boolean finished = false;

  SdrDance (GCustomSlider danceSlider, int start, int end, int danceSpeed) {
    slider = danceSlider;
    startPos = start;
    endPos = end;
    speed = danceSpeed;
  } 

  boolean finished() {
    return finished;
  }

  void update() {
    startPos = min(startPos + speed, endPos);
    finished = startPos == endPos;
    slider.setValue(startPos);
  }
}

//******GUI
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(5, 0, 180);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(5, 0, 180);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 175, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");
}

public void sdr1_change1(GCustomSlider source, GEvent event) {
} 

public void sdr2_change1(GCustomSlider source, GEvent event) {
} 

public void handleSliderEvents(GValueControl slider, GEvent event) {
}
2 Likes

you are awesome, thank you so much :star_struck:

I just break my sketch to small problems and i try to solve theme one by one, the sketch above is just an example to show the problem. It works perfectly and i will implement the solution in my sketch. i added startPos = max(startPos - speed, endPos); in the case startPos > endPos.
I like to use classes, because they make my sketch easy to clean and make changes.

Thank you for the explanation and for the tips as well.

I wish you a great day :blush: :man_technologist:

im trying now to load the values for the sliders from a table row by row. I would like to now to jump to the next row with the new values after the increment of the current rows are finished.

I tried some options which could work, but i think the draw()-loop doesn t like me :thinking:

I hope you could give me some tips again :hugs:

This ist the code:

import g4p_controls.*;

SdrDance dance1;
SdrDance dance2;

Position[] positions;
Table table;
Table tbldance1;

GCustomSlider sdr1, sdr2;
GButton btnDance;

float startPos, endPos ,speed1, speed2;
int rowCount, R1, R2, count;
//boolean buttonClicked;
 
void setup() { 
  size(400, 200);
  createGUI();
  
  tbldance1 = new Table();  
   tbldance1.addColumn("count", Table.INT);
   tbldance1.addColumn("R1", Table.INT);
   tbldance1.addColumn("R2", Table.INT);
}

void draw() {
 background (180);  
  if (dance1 != null && !dance1.finished()) {
    dance1.update();
  }
  if (dance2 != null && !dance2.finished()) {
    dance2.update();
  }
}

public void btnDance_click1(GButton source, GEvent event) { 
  positions = new Position[tbldance1.getRowCount()];
  rowCount =0;
   for (TableRow row : tbldance1.rows()){ 
      if (rowCount< tbldance1.getRowCount()) {
          row = tbldance1.getRow(rowCount);
            int R1 = row.getInt("R1");
            int R2 = row.getInt("R2");          
           positions[rowCount] = new Position(R1, R2);
           println("Rot:"+(rowCount +1) +"  "+ "R1:" + R1 + "  " + "R2:" + R2); 
           //println("before reading/loading the next row, the increments of Dance1 & Dance2 should be finished"); 
           // just the increments of the last row has been executed --> draw()-loop is activated after the enhanced for-loop in the buttonClick event...!
           
               speed1 = 1; 
               speed2 = 1;
               dance1 = new SdrDance(sdr1, sdr1.getValueI(), R1, speed1);
               dance2 = new SdrDance(sdr2, sdr2.getValueI(), R2, speed2);
        //how to increase rowCount to the next row after dance1 & dance2 are finisched?!! nested for-loop -, if statment -, boolean if finished -->how?                 
        rowCount++;
      }          
   }    
} 

void keyPressed(){  
 TableRow row = tbldance1.addRow();
  row.setInt("count", count++);
  row.setInt("R1",sdr1.getValueI());
  row.setInt("R2", sdr2.getValueI());
}

void exit(){
  for(TableRow row : tbldance1.rows() ){
    println( row.getInt(0), row.getInt(1), row.getInt(2));
  }
}

//*****Class 
class SdrDance {  
  GCustomSlider slider;
  float startPos;
  float endPos;
  float speed;
  boolean finished = false;

  SdrDance (GCustomSlider danceSlider, float start, float end, float danceSpeed) {
    slider = danceSlider;
    startPos = start;
    endPos = end;
    speed = danceSpeed;
  } 

  boolean finished() {
    return finished;
  }

  void update() {
  
     startPos = min(startPos + speed, endPos);       
   
    finished = startPos == endPos;
    slider.setValue(startPos);
  }
}

//******Class 
class Position {
  Position(int tempR1, int tempR2){ 
    R1 = tempR1;
    R2 = tempR2;
   }
}

//******GUI with G4P controls
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(10, 0, 180);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(10, 0, 180);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 175, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");
}

public void handleSliderEvents(GValueControl slider, GEvent event) {  }
public void sdr1_change1(GCustomSlider customslider, GEvent event) {  }
public void sdr2_change1(GCustomSlider customslider, GEvent event) {  }
public void handleButtonEvents(GButton button, GEvent event) {  }


I am not sure where to start and I can see many problems with the code but find it difficult to advise you because you have not clearly described what you hope to achieve.

Here is a list of requirements that I thought of, they might not be exactly want you want in which case come up with your own requirements list.

  1. The sliders must dance at the same time
  2. Each dance is associated with a single slider
  3. Each dance can have a different start position, end position and speed
  4. Start / end position and speed have integer values
  5. The start position will be different from the end position
  6. The dance can move the slider thumb left or right depending on the start and end positions specified
  7. A new dance is allocated to all sliders when all the sliders have finished their current dance.

Some points to consider -
Item (4) although you are using int you might find float provides smoother movement and higher granular control.
Item (6) I have not tested the update method to see if it will work in both directions.
Item (7) you cannot be certain that 2 dances have the same duration so do you want a slider to get a new dance even if the others haven’t finished.

The code you provide in the method public void btnDance_click1(GButton source, GEvent event) will never work without major changes.

So the first step is for you to provide your own list of requirements.

1 Like

Sorry Peter, im so deep in my sketch and i though, it is clear what i want to achieve :blush:

  1. The sliders must dance at the same time:
    yes, and with your help, ist is working now.

  2. Each dance is associated with a single slider:
    yes, we have implemented that also with the class SdrDance --> slider1 with Dance1 and so on.

  3. Each dance can have a different start position, end position and speed:
    yes, we have implemented that also
    Bonus --> it will be great if i could also slow down or speed up the Dance of each slider with a
    variable, maybe with delay() --> for example Delay_Dance1, Delay_Dance2 on so on.

  4. Start / end position and speed have integer values:
    thank you for the tipp --> i changed all the variables to float.

  5. The start position will be different from the end position:
    Maybe at the first Dance (beginning of the show) or maybe by the next Dance, start position and
    end position of some sliders or of all of them could be the same --> for example, it is like the
    Bellagio Fountains Show , instead of water, i have the sliders :blush:

  6. The dance can move the slider thumb left or right depending on the start and end positions specified:
    yes, i added startPos = max(startPos - speed, endPos); in the case startPos > endPos, it works

  7. A new dance is allocated to all sliders when all the sliders have finished their current dance:
    yes, the next Dance (next row from table) should start, after all the sliders have finished their
    current Dance (current row in the table).
    This is also the point, where i have a problem at the moment --> i added some comments in the
    sketch line 47-51 (the row-counter should increase after the current Dance of the all sliders is
    finished).

  8. It should be possible to add neu Sliders in the future. I just use 2 Sliders, so the code is not to
    much big in the forum…tips from the guidelines of the forum :blush:

  9. i also plan to add pause and stop buttons, if i want to pause (continue from where i paused) or
    stop (start from beginning) the show, but that is the next step :slight_smile:

  10. i actually load or save the table with the Dances but i just add the void keyPressed () to keep it
    keep it simple and easy for helpers so there is no need for a csv table to load.

This is the code i have till now:

import g4p_controls.*; // thank you Peter Lager :-)

SdrDance dance1;
SdrDance dance2;

Position[] positions;
Table table;
Table tbldance1;

GCustomSlider sdr1, sdr2;
GButton btnDance;

float startPos, endPos ,speed1, speed2, R1, R2;
int rowCount, count;
//boolean buttonClicked;
 
void setup() { 
  size(400, 200);
  createGUI();
  
  tbldance1 = new Table();  
   tbldance1.addColumn("count", Table.INT);
   tbldance1.addColumn("R1", Table.FLOAT);
   tbldance1.addColumn("R2", Table.FLOAT);
}

void draw() {
 background (180);  
  if (dance1 != null && !dance1.finished()) {
    dance1.update();
  }
  if (dance2 != null && !dance2.finished()) {
    dance2.update();
  }
}

public void btnDance_click1(GButton source, GEvent event) { 
  positions = new Position[tbldance1.getRowCount()];
  rowCount =0;
   for (TableRow row : tbldance1.rows()){ 
      if (rowCount< tbldance1.getRowCount()) {
          row = tbldance1.getRow(rowCount);
            float R1 = row.getFloat("R1");
            float R2 = row.getFloat("R2");          
           positions[rowCount] = new Position(R1, R2);
           println("Rot:"+(rowCount +1) +"  "+ "R1:" + R1 + "  " + "R2:" + R2); 
           //println("before reading/loading the next row, the increments of Dance1 & Dance2 should be finished"); 
           // just the increments of the last row has been executed --> draw()-loop is activated after the enhanced for-loop in the buttonClick event...!
           
               speed1 = 1.0; 
               speed2 = 1.0;
               dance1 = new SdrDance(sdr1, sdr1.getValueF(), R1, speed1);
               dance2 = new SdrDance(sdr2, sdr2.getValueF(), R2, speed2);
        //how to increase rowCount to the next row after dance1 & dance2 are finisched?!! nested for-loop -, if statment -, boolean if finished -->how?                 
        rowCount++;
      }          
   }    
} 

void keyPressed(){  
 TableRow row = tbldance1.addRow();
  row.setInt("count", count++);
  row.setFloat("R1",sdr1.getValueF());
  row.setFloat("R2", sdr2.getValueF());
}

void exit(){
  for(TableRow row : tbldance1.rows() ){
    println( row.getInt(0), row.getFloat(1), row.getFloat(2));
  }
}

//*****Class 
class SdrDance {  
  GCustomSlider slider;
  float startPos;
  float endPos;
  float speed;
  boolean finished = false;

  SdrDance (GCustomSlider danceSlider, float start, float end, float danceSpeed) {
    slider = danceSlider;
    startPos = start;
    endPos = end;
    speed = danceSpeed;
  } 

  boolean finished() {
    return finished;
  }

  void update() {
  
    if (startPos < endPos){
        startPos = min(startPos + speed, endPos);
    }
    else if (startPos > endPos){
            startPos = max(startPos - speed, endPos);
    }
    finished = startPos == endPos;
    slider.setValue(startPos);
  }
}

//******Class 
class Position {
  Position(float tempR1, float tempR2){ 
    R1 = tempR1;
    R2 = tempR2;
   }
}

//******GUI with G4P controls
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(10, 0, 180);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(10, 0, 180);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 175, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");
}

public void handleSliderEvents(GValueControl slider, GEvent event) {  }
public void sdr1_change1(GCustomSlider customslider, GEvent event) {  }
public void sdr2_change1(GCustomSlider customslider, GEvent event) {  }
public void handleButtonEvents(GButton button, GEvent event) {  }


It is by the way a great forum with so many nice examples and i really lern a lot here.
I really appreciate your time and your help :muscle:

1 Like

The setLimits method is overloaded so to make sure G4P realises you are using floats change 1 or more parameters to float e.g.

sdr1.setLimits(10f, 0, 180);
// or
sdr1.setLimits(10.0, 0, 180);

The real problem is then the button event handler and I will look at that shortly

i forgot that :face_with_hand_over_mouth:

I will clean up every thing later and make the final GUI with the G4P Builder. I think it is also possible to edit the value type decimal and also the numeric of precision with G4P Builder :ok_hand:

Internally G4P uses floats inside the slider classes, the difference is in how the slider reports the value back with getValue?() setting the number type and precision simply controls how the number is displayed in the slider GUI.

New sketch code to follow in a few minutes

I have made changes to the SdrDance class so that we can reuse the objects rather than create new objects for each row. I also create dance1 and dance2 in setup so we don’t have to test for null in draw()

Notice the button click method is now

public void btnDance_click1(GButton source, GEvent event) { 
  isDancing = true;
} 

Notice that we are not doing any real work here we are simply telling the program to start dancing.

I have also created some dummy data for the table in setup

In the code you don’t use the Position class but I have made a comment in the code
about it. I have converted the variable names R1 and R2 to r1 and r2 see one on my earlier. posts.

I think that’s it for the moment have a look at the code and ask questions if you get stuck.

import g4p_controls.*; // thank you Peter Lager :-)

SdrDance dance1;
SdrDance dance2;

Position[] positions;
Table table;
Table tbldance1;

GCustomSlider sdr1, sdr2;
GButton btnDance;

float startPos, endPos, speed1, speed2, r1, r2;
int rowCount, count;
boolean isDancing;

void setup() { 
  size(400, 200);
  createGUI();
  // Make the dances to avoid testing for null later
  dance1 = new  SdrDance(sdr1);
  dance2 = new  SdrDance(sdr2);
  tbldance1 = new Table();  
  tbldance1.addColumn("count", Table.INT);
  tbldance1.addColumn("R1", Table.FLOAT);
  tbldance1.addColumn("R2", Table.FLOAT);
  TableRow row;
  // Row 1
  row = tbldance1.addRow();
  row.setFloat("R1", 20);
  row.setFloat("R2", 60);
  // Row 2
  row = tbldance1.addRow();
  row.setFloat("R1", 80);
  row.setFloat("R2", 10);
  // Row 3
  row = tbldance1.addRow();
  row.setFloat("R1", 40);
  row.setFloat("R2", 180);
  // Init row count
  rowCount = -1;
  // Row 4
  row = tbldance1.addRow();
  row.setFloat("R1", 170);
  row.setFloat("R2", 40);
  // Init row count
  rowCount = -1;
}

void draw() { 
  background (180);  
  // Make sure we have clicked the button to start the dancing
  if (isDancing) {
    if (dance1.finished() && dance2.finished()) {
      // Both dances have finished so load the next one
      nextDance();
    } else {
      // Update the dancing sliders
      dance1.update();
      dance2.update();
    }
  }
}

// Move to the next dance in the table
public void nextDance() {
  // Next row
  rowCount++;
  // Loop through rows in table
  rowCount %= tbldance1.getRowCount();
  TableRow row = tbldance1.getRow(rowCount);
  r1 = row.getFloat("R1");
  speed1 = 1.2; 
  dance1.nextDance(r1, speed1);
  r2 = row.getFloat("R2");
  speed2 = 1.9;
  dance2.nextDance(r1, speed2);
}

public void btnDance_click1(GButton source, GEvent event) { 
  isDancing = true;
} 

void exit() {
  for (TableRow row : tbldance1.rows() ) {
    println( row.getInt(0), row.getFloat(1), row.getFloat(2));
  }
}

//*****Class 
class SdrDance {  
  private final GCustomSlider slider;
  private float startPos;
  private float endPos;
  private float speed;
  private boolean finished = false;

  SdrDance (GCustomSlider danceSlider) {
    slider = danceSlider;
    startPos = endPos = danceSlider.getValueF();
    speed = 0;
    finished = true;
  }

  boolean finished() {
    return finished;
  }

  void update() {
    if (startPos < endPos) {
      startPos = min(startPos + speed, endPos);
    } else if (startPos > endPos) {
      startPos = max(startPos - speed, endPos);
    }
    finished = startPos == endPos;
    slider.setValue(startPos);
  }

  void nextDance(float end, float danceSpeed) {
    startPos = slider.getValueF();
    endPos = end;
    speed = danceSpeed;
    finished = false;
  }
}

//******Class 
class Position {
  Position(float tempR1, float tempR2) { 
    // This is bad programming practice because r1 & r2 are not 
    // attributes / fields of the class.
    r1 = tempR1;
    r2 = tempR2;
  }
}

//******GUI with G4P controls
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(10, 0, 180);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(10, 0, 180);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 175, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");
}

public void handleSliderEvents(GValueControl slider, GEvent event) {
}
public void sdr1_change1(GCustomSlider customslider, GEvent event) {
}
public void sdr2_change1(GCustomSlider customslider, GEvent event) {
}
public void handleButtonEvents(GButton button, GEvent event) {
}
1 Like

that s it :star_struck: :man_dancing:

I don t understand the constructor in the class SdrDance yet but i will study the code again.
The fact, that we now use floats instead int, there is no need to slow down the speed with delays which i don t like :joy:.
I will implement the code in my sketch und i will probably come back in a few days with some other problems :face_with_hand_over_mouth:.

I forgot that again with the r1 in the class Position and thanks again for advice :man_teacher:

Thank you for help and have a great week :man_technologist:

Dear Peter, i implemented your code in my sketch and it works perfect but i still have 3 problems before i finish this project :grimacing: im trying to find a solution:

  1. how to stop the draw() loop when the show is finished, i mean when the the last dance (last row in the table) is finished, then the draw() loop should stop and not start from beginning!

  2. how to update the sketch so i can add a pause and a stop button. If i want to pause (continue from where i paused) or stop (start from beginning) the show.

I think, it will be difficult without making changes in the draw() loop. I tried some solutions but nothing works :thinking: !!!

Do you have an idea how i solve this?

thank you in advance :sweat_smile:

import g4p_controls.*; // thank you Peter Lager :-)

SdrDance dance1;
SdrDance dance2;

Table tbldance1;

GCustomSlider sdr1, sdr2;
GButton btnDance, btnPause, btnStop;

float startPos, endPos, speed1, speed2, r1, r2;
int rowCount, count;
boolean isDancing, isPaused, isStoped;

void setup() { 
  size(400, 200);
  createGUI();
  // Make the dances to avoid testing for null later
  dance1 = new  SdrDance(sdr1);
  dance2 = new  SdrDance(sdr2);
  tbldance1 = new Table();  
  tbldance1.addColumn("count", Table.INT);
  tbldance1.addColumn("R1", Table.FLOAT);
  tbldance1.addColumn("R2", Table.FLOAT);
  TableRow row;
  // Row 1
  row = tbldance1.addRow();
  row.setFloat("R1", 20);
  row.setFloat("R2", 60);
  // Row 2
  row = tbldance1.addRow();
  row.setFloat("R1", 80);
  row.setFloat("R2", 10);
  // Row 3
  row = tbldance1.addRow();
  row.setFloat("R1", 40);
  row.setFloat("R2", 180);
  // Init row count
  // Row 4
  row = tbldance1.addRow();
  row.setFloat("R1", 170);
  row.setFloat("R2", 40);
  // Init row count
  rowCount = -1;
}

void draw() { 
  background (180);  
  if (isDancing) {
    if (dance1.finished() && dance2.finished()) {
      //if (rowCount < tbldance1.getRowCount()){ // the show should stop (finish) when the last Dance (last row in the table) is finished without closing the sketch/Window 
      nextDance();
      //}
    } else {
      dance1.update();
      dance2.update();
    }
  }
}

// Move to the next dance in the table
public void nextDance() {
  // Next row
  //if (rowCount <= tbldance1.getRowCount()){
  rowCount++;
  // Loop through rows in table
  rowCount %= tbldance1.getRowCount();
  TableRow row = tbldance1.getRow(rowCount);
  r1 = row.getFloat("R1");
  speed1 = 1.0; 
  dance1.nextDance(r1, speed1);
  r2 = row.getFloat("R2");
  speed2 = 1.0;
  dance2.nextDance(r2, speed2);
  //}
  
 //if (rowCount > tbldance1.getRowCount()){
 //  exit();
   
 //}
}

public void btnDance_click1(GButton source, GEvent event) { 
  isDancing = true;
} 

public void btnPause_click1(GButton source, GEvent event) { 
  //isPaused = true;
} 

public void btnStop_click1(GButton source, GEvent event) { 
  //isStoped = true;
} 

//*****Class 
class SdrDance {  
  private final GCustomSlider slider;
  private float startPos;
  private float endPos;
  private float speed;
  private boolean finished = false;

  SdrDance (GCustomSlider danceSlider) {
    slider = danceSlider;
    startPos = endPos = danceSlider.getValueF();
    speed = 0;
    finished = true;
  }

  boolean finished() {
    return finished;
  }

  void update() {
    if (startPos < endPos) {
      startPos = min(startPos + speed, endPos);
    } else if (startPos > endPos) {
      startPos = max(startPos - speed, endPos);
    }
    finished = startPos == endPos;
    slider.setValue(startPos);
  }

  void nextDance(float end, float danceSpeed) {
    startPos = slider.getValueF();
    endPos = end;
    speed = danceSpeed;
    finished = false;
  }
}

//******GUI with G4P controls
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(10.0, 0.0, 180.0);
  sdr1.setNumberFormat(G4P.DECIMAL, 1);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(10.0, 0.0, 180.0);
  sdr2.setNumberFormat(G4P.DECIMAL, 1);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 100, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");
  
  btnPause = new GButton(this, 175, 15, 55, 25);
  btnPause.setText("Pause");
  btnPause.addEventHandler(this, "btnPause_click1");
  
  btnStop = new GButton(this, 250, 15, 55, 25);
  btnStop.setText("Stop");
  btnStop.addEventHandler(this, "btnStop_click1");
}


public void handleSliderEvents(GValueControl slider, GEvent event) {
}
public void sdr1_change1(GCustomSlider customslider, GEvent event) {
}
public void sdr2_change1(GCustomSlider customslider, GEvent event) {
}
public void handleButtonEvents(GButton button, GEvent event) {
}
1 Like

I believe the sketch below does what you want. I have had to modify the SdrDance class to remember the very first value of the slider so we can restart after a stop. The othe changes are simple enough and you should be able to make sense of them.

Just one thing about G4P and event handlers. G4P will look for generic event handlers for each control type e.g.

public void handleSliderEvents(GValueControl slider, GEvent event) {
}
public void handleButtonEvents(GButton button, GEvent event) {
}

These are not needed if each control has it’s own event handler using addEventHandler so you can delete them to prevent the warning messages about these being missing add the statement
G4P.messagesEnabled(false);
inside setup. I have done this for the code below.

import g4p_controls.*; // thank you Peter Lager :-)

SdrDance dance1;
SdrDance dance2;

Table tbldance1;

GCustomSlider sdr1, sdr2;
GButton btnDance, btnPause, btnStop;

float startPos, endPos, speed1, speed2, r1, r2;
int rowCount, count;
boolean isPaused = true, isStoped = true;

void setup() { 
  size(400, 200);
  G4P.messagesEnabled(false);
  createGUI();
  // Make the dances to avoid testing for null later
  dance1 = new  SdrDance(sdr1);
  dance2 = new  SdrDance(sdr2);
  tbldance1 = new Table();  
  tbldance1.addColumn("count", Table.INT);
  tbldance1.addColumn("R1", Table.FLOAT);
  tbldance1.addColumn("R2", Table.FLOAT);
  TableRow row;
  // Row 1
  row = tbldance1.addRow();
  row.setFloat("R1", 20);
  row.setFloat("R2", 60);
  // Row 2
  row = tbldance1.addRow();
  row.setFloat("R1", 80);
  row.setFloat("R2", 10);
  // Row 3
  row = tbldance1.addRow();
  row.setFloat("R1", 40);
  row.setFloat("R2", 180);
  // Init row count
  // Row 4
  row = tbldance1.addRow();
  row.setFloat("R1", 170);
  row.setFloat("R2", 40);
  // Init row count
  rowCount = -1;
}

void draw() { 
  background (180); 
  if (!isPaused && !isStoped) {
      if (dance1.finished() && dance2.finished()) {
        nextDance();
      } else {
        dance1.update();
        dance2.update();
      }
  }
}

// Move to the next dance in the table
public void nextDance() {
  // Next row
  rowCount++;
  if (rowCount < tbldance1.getRowCount()) {
    TableRow row = tbldance1.getRow(rowCount);
    r1 = row.getFloat("R1");
    speed1 = 1.0; 
    dance1.nextDance(r1, speed1);
    r2 = row.getFloat("R2");
    speed2 = 1.0;
    dance2.nextDance(r2, speed2);
  }
  else {
    isStoped = true;
    rowCount = -1;
    dance1.finished(true);
    dance2.finished(true);
  }
}

public void btnDance_click1(GButton source, GEvent event) { 
  dance1.resetDancing();
  dance2.resetDancing();
  isPaused = isStoped = false;
} 

public void btnPause_click1(GButton source, GEvent event) { 
  isPaused = !isPaused; // flip pause state
} 

public void btnStop_click1(GButton source, GEvent event) { 
  isStoped = true;
  dance1.finished(true);
  dance2.finished(true);
} 

//*****Class 
class SdrDance {  
  private final GCustomSlider slider;
  private float initPos;
  private float startPos;
  private float endPos;
  private float speed;
  private boolean finished = false;

  SdrDance (GCustomSlider danceSlider) {
    slider = danceSlider;
    initPos = startPos = endPos = danceSlider.getValueF();
    speed = 0;
    finished = true;
  }

  boolean finished() {
    return finished;
  }

  void finished(boolean f) {
    finished = f;
  }

  void update() {
    if (startPos < endPos) {
      startPos = min(startPos + speed, endPos);
    } else if (startPos > endPos) {
      startPos = max(startPos - speed, endPos);
    }
    finished = startPos == endPos;
    slider.setValue(startPos);
  }

  void nextDance(float end, float danceSpeed) {
    startPos = slider.getValueF();
    endPos = end;
    speed = danceSpeed;
    finished = false;
  }
  
  void resetDancing(){
    startPos = endPos = initPos;
    slider.setValue(startPos);
    finished = false;
  }
}

//******GUI with G4P controls
void createGUI() {
  sdr1 = new GCustomSlider(this, 100, 60, 200, 33);
  sdr1.setShowValue(true);
  sdr1.setLimits(10.0, 0.0, 180.0);
  sdr1.setNumberFormat(G4P.DECIMAL, 1);
  sdr1.addEventHandler(this, "sdr1_change1");

  sdr2 = new GCustomSlider(this, 100, 120, 200, 33);
  sdr2.setShowValue(true);
  sdr2.setLimits(10.0, 0.0, 180.0);
  sdr2.setNumberFormat(G4P.DECIMAL, 1);
  sdr2.addEventHandler(this, "sdr2_change1");

  btnDance = new GButton(this, 100, 15, 55, 25);
  btnDance.setText("Dance");
  btnDance.addEventHandler(this, "btnDance_click1");

  btnPause = new GButton(this, 175, 15, 55, 25);
  btnPause.setText("Pause");
  btnPause.addEventHandler(this, "btnPause_click1");

  btnStop = new GButton(this, 250, 15, 55, 25);
  btnStop.setText("Stop");
  btnStop.addEventHandler(this, "btnStop_click1");
}

public void sdr1_change1(GCustomSlider customslider, GEvent event) {
}
public void sdr2_change1(GCustomSlider customslider, GEvent event) {
}
1 Like