Scale up and down an ellipse doesn't work within a function

hi there
i found a possibility to make a maske for a livecam which scales from small to big and back. now i tryed to write it in a function. unfortunately it doesn’t work propper and i have no idea why. it must be something with the startTime and duration. within a function it says «The local variable startTime may not have been initialized» but if i do so, it doesn’t work at all. so i took the lastReadTime which is already used by the code anyway. but with this the scaling doesn’t work propper. it flickers and at the end it scales
only for a short moment. has anybody an idea why? here ist my code - thanks for help - cheers

import processing.video.*;
import processing.sound.*;

Capture cam;
PImage img;
PImage imgMask;
/*
float startSize = 1; // Anfangsgröße der Ellipse (200x200)
float targetSize = 300; // Zielgröße der Ellipse (10x10)
float currentSize = startSize; // Aktuelle Größe der Ellipse
float duration = 9000; // Dauer der Animation in Millisekunden (2 Sekunden)
float startTime; // Zeitpunkt, an dem die Animation gestartet wird
*/
Amplitude amp;
AudioIn in;

// two movie objects
Movie myMovie;
String fileMovie1 = "auge_drehen_25fps_conv.mp4";
Movie yourMovie;
String fileMovie2 = "auge_1080_verzerren_3_conv.mp4";
// movie that is currently playing
Movie currentMovie;

// last time that a frame was read
int lastReadTime = 0;
// timeout for play-once movie; 1 second
int readTimeout = 1000;


void setup()
{
 size(900, 900);
  fullScreen();
  background(0,11,51);
 // frameRate(30);
  //--------------------------------
    String[] cameras = Capture.list();
  
  if (cameras.length == 0) {
    println("There are no cameras available for capture.");
    exit();
  } else {
    println("Available cameras:");
    printArray(cameras);//gibt die kameranummer aus
    for (int i = 0; i < cameras.length; i++) {
      //println(cameras[i]);
    }
    
    // The camera can be initialized directly using an 
    // element from the array returned by list():
    cam = new Capture(this, cameras[1]);
    cam.start();     
  }      
    
  //--------------------------------

  amp = new Amplitude(this);
  in = new AudioIn(this, 0);
  //start the microphone
  in.start();
  amp.input(in);

  // first movie, will play continously
  myMovie = new Movie(this, fileMovie1);
  // second movie, plays once after clap
  yourMovie = new Movie(this, fileMovie2);

  // set the current movie to myMovie
  currentMovie = myMovie;
  // and loop it
  currentMovie.loop();
  
  img = loadImage("auge4-kl-maske.png"); // Lade das Hintergrundbild (ersetze "background_image.jpg" mit dem Dateinamen deines Bildes)
  img.resize(width, height); // Skaliere das Hintergrundbild auf die Größe der Leinwand
  //startTime = millis();
  noStroke(); // Entferne die Konturlinie

}

void draw()
{
   if (cam.available() == true) {
    cam.read();
  }

  imageMode(CENTER);

  image(currentMovie, width/2,height/2,900,900);
 
  // switch to
  soundDetector();
 
  // if the current movie is yourMovie which plays only once
  if (currentMovie == yourMovie)
  {
    // check if we did not have data for a specified time
    if (millis() - lastReadTime >= readTimeout)
    {
               
      println("Switching back to 'myMovie'");
      // stop the current movie (yourMovie) so a new play will start it from the beginning
      currentMovie.stop();
      // switch to the looping movie (myMovie)
      currentMovie = myMovie;
    
        // and (continue) playing
      currentMovie.play();       
    }
       maske(); 
 }      
}

void maske(){
//mmmmmmmmmmmmmmmmmmm grafikmaske einschalten

float startSize = 1; // Anfangsgröße der Ellipse (200x200)
float targetSize = 300; // Zielgröße der Ellipse (10x10)
float currentSize = startSize; // Aktuelle Größe der Ellipse
float duration = 2000; // Dauer der Animation in Millisekunden (2 Sekunden)
float startTime; // Zeitpunkt, an dem die Animation gestartet wird
 
startTime = lastReadTime;  

    // Berechne den Fortschritt der Animation (0.0 bis 1.0)
 float t = min(1.0, (millis() - startTime) / duration); 
 
   // Berechne die aktuelle Größe der Ellipse mithilfe von Interpolation
  if (t < 0.5) {
    // Vergrößern (von startSize zu targetSize)
    t = t * 2; // Verdopple den Fortschritt, um eine reibungslose Vergrößerung zu erreichen
    currentSize = lerp(startSize, targetSize, t);
  } else {
    // Verkleinern (von targetSize zu startSize)
    t = (t - 0.5) *2; // Verschiebe den Fortschritt, um eine reibungslose Verkleinerung zu erreichen
    currentSize = lerp(targetSize, startSize, t);
  }
 
println(min(1.0, (millis() - startTime) / duration)," - ", t);

  PGraphics maskPG = createGraphics(cam.width, cam.height);
  maskPG.beginDraw();
  maskPG.background(0);
  maskPG.fill(255);
  maskPG.noStroke();
  maskPG.ellipse(320,240, 640, 480 );//halbe cameragrösse für x und y und ganze kameragrösse für form = rund
  maskPG.endDraw();   
  cam.mask(maskPG);
 
 //   image(cam,0,0);
  // Überlagerung der Maske (transparentes Rechteck)
  translate(width/2,height/2);
  fill(255, 150); // Setze die Füllfarbe des transparenten Rechtecks (Alpha = 150)
  imageMode(CENTER);
  image(cam,0,0,currentSize,currentSize);
    
  //mmmmmmmmmmmmmmmmmmm   
  
}

//Sound dection; start other movie if a sounc is detected
void soundDetector()
{
  // if a clap was detected
  if (amp.analyze() > 0.015){
 
      // if we were playing the (looping) myMovie
      if (currentMovie == myMovie) {     
        println("Switching to 'yourMovie'");
        // pause the looping movie (myMovie) so the next time that we play it, it starts where it paused
        currentMovie.pause();
        // change the movie to yourMovie
        currentMovie = yourMovie;
        // and play it
        currentMovie.play();     
      }    
    }          
}

// Called every time a new frame is available to read
void movieEvent(Movie m)
{
  // keep track of the last time that we read a frame
  lastReadTime = millis();

  // read frame depending on movie that provided the frame
  if (m == myMovie)
  {
    myMovie.read();
  } //
  else if (m == yourMovie)
  {
    yourMovie.read();
  }
}

Hallo, jung programmer.

Please, isolate your problem and be more specific with your question. Pasting the whole code and expecting others to read through it is not going to farm you replies.

See Guidelines—Asking Questions

ok - sorry - my problem is that what i’m looking for, works outside of a more complex code. but as soon as i integrate it into a nested code, unfortunately it doesn’t work anymore. that’s why i uploaded all the code. and i was interested in a new way to solve the problem.
in the meantime i found a possiblity that works with a click-function.

now i have a new problem, because i would like to stop the growing ellipse when it is the biggest size for a few seconds before it shrinks again. here ist the breakout of the code concerning the problem.

startTime = lastReadTime;  

    // Berechne den Fortschritt der Animation (0.0 bis 1.0)
 float t = min(1.0, (millis() - startTime) / duration); 
 
   // Berechne die aktuelle Größe der Ellipse mithilfe von Interpolation
  if (t < 0.5) {
    // Vergrößern (von startSize zu targetSize)
    t = t * 2; // Verdopple den Fortschritt, um eine reibungslose Vergrößerung zu erreichen
    currentSize = lerp(startSize, targetSize, t);
  } else {
    // Verkleinern (von targetSize zu startSize)
    t = (t - 0.5) *2; // Verschiebe den Fortschritt, um eine reibungslose Verkleinerung zu erreichen
    currentSize = lerp(targetSize, startSize, t);
  }

Add a second millis() ‘timer’ and a (boolean) flag.

When the ellipse reaches its maximum size you set the flag and the store the time that it happened.

If the flag is set, don’t modify the ellipse till such time that the specified duration has lapsed. After that, continue.

Below code for demonstration purposes using two ‘timers’; it’s not based on your code.

int diameter = 0;
int maxDiameter = 200;
int direction = 1;

// last time that ellipse was updated
int lastCycleTime;
// modify circle every 100 ms
int cycleInterval = 100;

// keep track when circle reached maximum size
int waitStartTime;
// keep circle at maximum size for 5 seconds
int waitDuration = 5000;
// flag
boolean isMax = false;

void setup()
{
  size(300, 300);
}


void draw()
{
  background(0xFF404040);
  maske();
}

void maske()
{
  // draw ellipse
  fill(0xFF000000);
  ellipse(width / 2, height / 2, diameter, diameter);

  // if the ellipse did reach it maximum size, delay a bit
  if (isMax == true)
  {
    if (millis() - waitStartTime >= waitDuration)
    {
      // clear the flag
      isMax = false;
      println("delay lapsed @ " + str(millis()));
    }
  }

  // if the ellipse is not at its maximum size and it's time to update it
  if (isMax == false && millis() - lastCycleTime >= cycleInterval)
  {
    lastCycleTime = millis();
    // grow/shrink ellipse for the next update
    diameter += (2 * direction);

    // if ellipse is at maximum size
    if (diameter >= maxDiameter)
    {
      // set flag
      isMax = true;
      // remember the start time
      waitStartTime = millis();
      println("delay started @ " + str(millis()));
    }

    if (diameter < 0 || diameter >= maxDiameter)
    {
      // change from grow to shrink ort vice versa
      direction *= -1;
    }
  }
}

hi @sterretje
thank you for your script - i could integrate it in my code so far. but since i need it in combination with a mousePressed, it should start over, as soon as i press the mouse. but it contiues because it references to the main time of the script. i built in a timer into the maske function. the timer works as wished, but not the ellipse. what am i doing wrong?

int diameter = 0;
int maxDiameter = 750;
int direction = 7;
int startTime;

// last time that ellipse was updated
int lastCycleTime;
// modify circle every 100 ms
int cycleInterval = 10;

// keep track when circle reached maximum size
int waitStartTime;
// keep circle at maximum size for 5 seconds
int waitDuration = 2000;
// flag
boolean isMax = false;

int clickTime = 0; // Variable to store the time of the click
boolean clicked = false; // Indicator variable to check if a click has occurred

void setup()
{
  size(900, 900);
}

void draw()
{
  background(0xFF404040);   
  if(mousePressed){    
    zeitangabe();
    clicked = true;
    maske();
  }
}

void maske()
{

   clickTime = millis();
  
  // draw ellipse
  fill(0xFF000000);
  ellipse(width / 2, height / 2, diameter, diameter);

  // if the ellipse did reach it maximum size, delay a bit
  if (isMax == true)
  {
    if (millis() - waitStartTime >= waitDuration)     
    {
      // clear the flag
      isMax = false;
      println("delay lapsed @ " + str(millis()));     
    }
  }

  // if the ellipse is not at its maximum size and it's time to update it
  if (isMax == false && millis() - lastCycleTime >= cycleInterval)
  
  {
   // lastCycleTime = millis();
    lastCycleTime = clickTime;
    // grow/shrink ellipse for the next update
    diameter += (2 * direction);

    // if ellipse is at maximum size
    if (diameter >= maxDiameter)
    {
      // set flag
      isMax = true;
      // remember the start time
      waitStartTime = millis();
      println("delay started @ " + str(millis()));
    }

    if (diameter < 0 || diameter >= maxDiameter)
    {
      // change from grow to shrink ort vice versa
      direction *= -1;
    }
  }
}

void zeitangabe(){
   if (clicked) {
    long elapsedTime = millis() - clickTime;
    fill(255);
    textAlign(CENTER);
    textSize(24);
    text("Time elapsed: " + elapsedTime + " ms", width / 2, 50);
  } 
}

I’m not sure if I understand what you exactly want to achieve. Can you give a good description?

E.g.
What should trigger the drawing of the circle? The mouse press?
Should it continue forever growing and shrinking or should it stop when you release the mouse? If the latter, should the circle start with diameter 0 again or continue from the last size?

Add as many details as you can think of.

hi sterretje
if everything works together at the end of the project, a radar sensor will be the trigger. the value i get from the sensor is 0 or 1. this corresponds to the mousePressed. when the mouse is pressed, the animation runs with the ellipse. when the mouse is released and the mouse is pressed again, the animation should start again at 0.
at the moment the animation continues to run in the background. but it shouldn’t. it should start again from the beginning with every click.

for clarification of the whole project: the ellipse is representative for a live image of a camera that is triggered via sensor. this is also the reason why everything has to be inside of one function.
what i also tested is to make the edges of the ellipse a bit softer. for that i used the filter() with the function blure. but that had the consequence that everything gets totally slow. so it’s probably not the right approach? besides it only worked with the ellipse but not anymore if i replace the ellipse with the livecamera.
and on the top, it would be great if i could fade in the ellipse resp. livecamera while it’s getting bigger and then fade it out again shortly before it ist smallest again. but that probably seems to be too complicated?

In the draw() method add an else to the if(mousePressed). In that else you can

  1. Set the diameter back to zero so the next time the circle will start from size 0.
  2. Set the direction to the positive value so the circle will grow again.

I did notice that you changed the initial value of the direction variable; That is not the intention (though it does work); that variable is only to indicate grow or shrink. In diameter += (2 * direction); the ‘2’ indicates the speed. You can use a new variable for that or modify the value.

Your variable clicked is not very useful at this stage. You set it but never clear it; further it is set in the if and maske() is only called in the if.

thank you so much for your advice. now it works as wished. the only thing i don’t understand is the last part about the clicked. i don’t understand, what you mean with clear it and how it is called. but i see, that it doesn’t work propper. i just want to see, how long the animation takes. but it counts some weird numbers.

clicked = true sets the clicked variable, clicked = false clears the clicked variable.

In the code in post #5 you set it to true in draw() when the mouse is pressed and next you call maske() where you check if it’s set to true. But that will always be the case so as the code stands now the variable has no purpose.

with the mousePressed i would like to see, how many secondes the animation of the growing and shrinking ellipse takes. i thought, that i do this with the clicked=true and the function zeitangabe() which writes the elapsed time since the mouse is pressed. but it always jumps back an flickers somewhere between 15 and 18 milliseconds - which doesn’t make sense at all. but i don’t know, how to write it proper. i tried to place the clickTime = millis(), (which means, that the millis are written in the variable clickTime), somewhere else in the code, but it doesn’t work at all. what am i doing wrong?

I think that what you’re looking for is a detection of the change in state of the mouse, not a detection if the mouse is pressed or not pressed.

You can add a global variable lastMouseState and that you can compare with the current mouse state (mousePressed). The beginning of your code will now look like

// diameter of circle to dislay
int diameter = 0;
// max diameter
int maxDiameter = 750;
// direction; 1 -> grow circle, -1 -> shrink circle 
int direction = 1;
// speed at which the circle must grow / shrink
int speed = 14;

// last time that ellipse was updated
int lastCycleTime;
// modify circle every 100 ms
int cycleInterval = 10;

// keep track when circle reached maximum size
int waitStartTime;
// keep circle at maximum size for 5 seconds
int waitDuration = 2000;
// flag
boolean isMax = false;

int clickTime = 0; // Variable to store the time of the click
boolean clicked = false; // Indicator variable to check if a click has occurred

// remember the last state of the mouse
boolean lastMouseState = false;

In 'draw()` you can now do the compare

  // detect a change in mouse state
 if (mousePressed != lastMouseState)
 {
   // remember the new mouse state
   lastMouseState = mousePressed;
   println("mouse state changed");
 }

You can now extend the code that is executed if the state changed (your variable clicked now comes in handy :wink:).

  // detect a change in mouse state
  if (mousePressed != lastMouseState)
  {
    // remember the new mouse state
    lastMouseState = mousePressed;
    print("mouse state changed; ");

    // if the new stae is 'pressed'
    if (mousePressed == true)
    {
      println("mouse pressed");
      // remember the time
      clickTime = millis();
      // set the flag
      clicked = true;
    } //
    else
    {
      println("mouse released");
      // reset variables
      direction = 1;
      diameter = 0;
      // no longer clicked
      clicked = false;
    }
  }

So when the state changed (from not pressed to pressed), you remember the time that that happend and set the clicked flag to true. So when the state changed (from pressed to not pressed), you clear the variables.

Next you can test the clicked variable and call zeitangabe and maske.

  if (clicked == true)
  {
    zeitangabe();
    maske();
  }

Full code below; I’ve added a 1speed1 variable that is used to indicate how fast the circle must grow / shrink), added some more comments and removed some redundant stuff.

// diameter of circle to dislay
int diameter = 0;
// max diameter
int maxDiameter = 750;
// direction; 1 -> grow circle, -1 -> shrink circle 
int direction = 1;
// speed at which the circle must grow / shrink
int speed = 14;

// last time that ellipse was updated
int lastCycleTime;
// modify circle every 100 ms
int cycleInterval = 10;

// keep track when circle reached maximum size
int waitStartTime;
// keep circle at maximum size for 5 seconds
int waitDuration = 2000;
// flag
boolean isMax = false;

int clickTime = 0; // Variable to store the time of the click
boolean clicked = false; // Indicator variable to check if a click has occurred

// remember the last state of the mouse
boolean lastMouseState = false;



void setup()
{
  size(900, 900);
}

void draw()
{
  background(0xFF404040);

  // detect a change in mouse state
  if (mousePressed != lastMouseState)
  {
    // remember the new mouse state
    lastMouseState = mousePressed;
    print("mouse state changed; ");

    // if the new stae is 'pressed'
    if (mousePressed == true)
    {
      println("mouse pressed");
      // remember the time
      clickTime = millis();
      // set the flag
      clicked = true;
    } //
    else
    {
      println("mouse released");
      // reset variables
      direction = 1;
      diameter = 0;
      // no longer clicked
      clicked = false;
    }
  }


  if (clicked == true)
  {
    zeitangabe();
    maske();
  }
}

void maske()
{
  // draw ellipse
  fill(0xFF000000);
  ellipse(width / 2, height / 2, diameter, diameter);

  // if the ellipse did reach it maximum size, delay a bit
  if (isMax == true)
  {
    if (millis() - waitStartTime >= waitDuration)
    {
      // clear the flag
      isMax = false;
      println("delay lapsed @ " + str(millis()));
    }
  }

  // if the ellipse is not at its maximum size and it's time to update it
  if (isMax == false && millis() - lastCycleTime >= cycleInterval)
  {
    // remember the time that we did update
    lastCycleTime = millis();
    // grow/shrink ellipse for the next update
    diameter += (speed * direction);

    // if ellipse is at maximum size
    if (diameter >= maxDiameter)
    {
      // set flag
      isMax = true;
      // remember the start time
      waitStartTime = millis();
      println("delay started @ " + str(millis()));
    }

    if (diameter < 0 || diameter >= maxDiameter)
    {
      // change from grow to shrink ort vice versa
      direction *= -1;
    }
  }
}

void zeitangabe()
{
  long elapsedTime = millis() - clickTime;
  fill(255);
  textAlign(CENTER);
  textSize(24);
  text("Time elapsed: " + elapsedTime + " ms", width / 2, 50);
}

Hello @blindschleiche,

An example using mousePressed() and mouseReleased():


boolean clicked = false; // Indicator variable to check if a click has occurred

int x, y;

// Time t millis() 
int tStart;
int tEnd;
int tElapsed;

// Framecount f frames
int fStart;
int fEnd;
int fElapsed;

void setup()
  {
  size(320, 360);
  textSize(24);
  }

void draw()
  {
  background(64);
   
  if(clicked)
    {    
    x++; y++;
    if (50+x>width-50)
      {
      x=0; y=0;
      }
    tElapsed = millis() - tStart;
    fElapsed = frameCount - fStart;
    }
  stroke(255);
  strokeWeight(2);
  line(50, 100, 50+x, 100+y);
  text("Time Elapsed: " + str(tElapsed) + "  ms", 20, 30);
  text("Frames Elapsed: " + str(fElapsed) + "  frames", 20, 60);
  }

void mousePressed()
  {
  clicked = true;
  tStart = millis();
  fStart = frameCount;
  }
  
void mouseReleased()
  {
  clicked = false;
  tEnd = millis();
  fEnd = frameCount;
  println(tStart, tEnd, tEnd -tStart); // Testing
  println(fStart, fEnd, fEnd -fStart); // Testing
  println();
  }

References:

:)

@glv , the problem is that the mousePress at this stage is a simulation of some external input. Hence I did not go that way. See below comment.

hi @sterretje
thank you for your explanation and the code. now i understand it better. since the function has to work in a greater code, i tested it. unfortunately the animation starts over again as long as the mouse is pressed. on one side that’s what it has to do. on the other side should the animation run through only once. so i added in your script after «direction *= -1;

    if (diameter == 0) {
      direction = 0;
    }

this way it stops after one runthrough. but if i integrate it in the bigger code, this doesn’t work anymore. is there another way to stop the animation after one runthrough?

What do you want to do. Only grow and then stop? On cycle grow and shrink and stop?

Your request in an earlier post was to add a wait period after the circle reached the maximum size. Does that no longer apply?

but yes, the wait period after the circle reached the maximum size is still necessary.
the ellipse should first grow, then wait for a certain time at maximum size, then become small again and stop. this is one runthrough. this is the process that is necessary.
now i have problems with the stop and start time within the whole script. because it doesn’t stop after one runthrough precisely, it seems not to start at the beginning anymore too. somewhere happens a timeshift…

Hello @blindschleiche,

Animation is from code that:

  • masks a video with an image of a circle
  • circle grows when mouse is pressed
  • pauses at max size for 3 seconds
  • circle shrinks until mouse released
  • displays meaningful stats
  • video only plays while mouse pressed
  • yellow circle fading in and out was an embellishment

test2

A refresh of web page may be necessary to play again.

I started from scratch and used code from my example and Processing movie loop library example. I am already familiar with masking.

I set the above as a goal to achieve and worked towards that.

This is achievable… keep working at it!

Sometimes you need to start from scratch and build on that.

It helps to list with clarity the steps that you are trying to achieve.

:)

So the sequence is as below ?

  1. wait for trigger
  2. grow to max
  3. wait a bit
  4. shrink to zero
  5. back to (1)

Can you show your attempt at achieving that?

yes exactly the way you describe it. the problem is, that it works perfect within the example above, but not anymore within a complexer script. do you want to see the whole script?