Constrain one end of sine wave

I’m trying to figure out how to constrain one end of a sine wave so that the left end of the wave animation is “tied” to the mouse x and mouse y. I’m thinking i need PVectors, but not sure how to implement that. I’ve working off of Shiffman’s sine wave example. I have it “kinda” working with translate, but not really what i want (I want to be able to move the wave around as if it’s attached to the mouse)

int xspacing = 2;   // How far apart should each horizontal location be spaced
int w;              // Width of entire wave

float theta = 0.0;  // Start angle at 0
float amplitude;
float x;

float period = 900.0;  // How many pixels before the wave repeats
float dx;  // Value for incrementing X, a function of period and xspacing
float[] yvalues;  // Using an array to store height values for the wave  

float r;//random values
float mx, my;


void setup(){
  size(800,600,P3D);
  frameRate(30);
  background(255);
  noCursor();
  
  w = width;
  dx = (TWO_PI / period) * xspacing;
  yvalues = new float[w/xspacing];
}

void draw(){
  fill(255,100);
  rect(0,0,width,height);
  mx=mouseX;
  my=mouseY;

  strokeWeight(1);
  stroke(10);
  fill(255);
  ellipse(mx,my,10,10);
  calcWave();
  renderWave();
 
}


void calcWave() {
 
   theta +=.05;
   amplitude= random(70,72);
 
  // For every x value, calculate a y value with sine function
   x = theta;
   
  for (int i = 0; i < yvalues.length; i++) {
   yvalues[i] = sin(x)*amplitude;
    x+=dx; 
  }
}

void renderWave() {
  
  noStroke();
  fill(10);
  r=random(height);
   pushMatrix();
    translate(mouseX+10,mouseY-200);
    // A simple way to draw the wave with an ellipse at each location
    for (int x = 0; x < yvalues.length; x++) {
      ellipse(x*xspacing, height/2+yvalues[x], 2, 2);  
  }
  
 popMatrix();
}

Hi brewthom,

I simplified a bit your code by getting rid of your calcWave() and renderWave() function and by drawing the sine wave directly with point() without storing the values in an array but you can of course rearrange everything the way you like.

The idea is to separate the time aspect from the space aspect.

you can get the y coordinate corresponding to an x coordinate with the following formula:

y(x) = amp * sin(freq * x)

Let’s take amp = 0.5 and freq = 0.5 * PI for the sake of simplicity. Then you have the following figure:

image

To reproduce this result in processing, you can simply draw points every dx pixels on the screen like so:

// Compute the value of the sine wave every 2 pixels and draw a point at that position
for (int x = 0; x < width; x += 2) {
  float y = 0.5 * sin(0.5 * PI * x);
  point(x, y);
}

Now, let’s say you want to move this sine to your mouse position and your mouse is at mx = 2 and my = 1.
In that case the point [0, 0] will be at postion [2, 1], the point [1, 0.5] will be at position [3, 1.5], the point [2, 0] will be at position [4, 1] and so on…
Can you see how, using the initial value, we simply add 2 to x and 1 to y to get the desired result?
To do the same in our code, we simply have to offset our points in the point() function:

// Compute the value of the sine wave every 2 pixels and draw a point at that position
for (int x = 0; x < width; x += 2) {
  float y = 0.5 * sin(0.5 * PI * x);
  point(x + 2, y + 1);
}

image

We got the space aspect figured out. Now we want to deal with the time aspect. For this one, what we want to do is shift the wave on the left after each loop. For example after some time we want that first peak at [1, 0.5] (in the first figure) to be shifted to the left to be at x = 0.
So what we want is to get the y value we are getting when x = 1 but when x = 0. So we simply need to add 1 to x. So when x = 0, x + 1 = 0, so y = 0.5. This way we have our point [0, 0.5].
Since it needs to move over time, we won’t always add one, but a variable, let’s call it t that we will increment over time like so:

float t; // The current time
final float dt = 10; // The speed of the movement

void loop() {
  for (int x = 0; x < width; x += 2) {
    float y = 0.5 * sin(0.5 * PI * (x + t)); // We add t to x to compute y
    point(x, y);
  }
  t+=dt; // After drawing the first sine, we increment 't' to make it moves
}

When you combine all that together we get the following code:

final float period = 400.0;  // How many pixels before the wave repeats
final float freq = TWO_PI / period;
final float amp = 50;
final float dx = 2;
final float dt = 10;
float t;


void setup(){
  size(800, 600);
  frameRate(30);
  t = 0;
}


void draw(){
  background(20);
  strokeWeight(2);
  stroke(220);
  
  float mx = mouseX;
  float my = mouseY;
  
  for (int x = 0; x < width; x += dx) {
    float y0 = amp * sin(freq * (x + t));
    point(x + mx, y0 + my);
  }
  
  t += dt;
}
2 Likes

Thanks for the breakdown. I’m also wondering it it’s possible to keep one end of the sine wave stationary? (Like it’s tied down)

Hi @brewthom ,

You can achieve that by subtracting the first y value so it stays stationary with the mouse :

float firstY = amp * sin(freq * t); // y0 position with x = 0
  
for (int x = 0; x < width; x += dx) {
  float y0 = amp * sin(freq * (x + t)) - firstY;
  point(x + mx, y0 + my);
}
1 Like