animating sin waves in processing

孤街浪徒 提交于 2019-12-12 18:36:13

问题


how do I animate the sin lines in the following code to move along the y-axis, to somehow look more like moving water waves?

-if you take out the velocity and acceleration codes you will see what I was trying to work with

float scaleVal = 6.0;
float angleInc = 0.19;

float velocity=0.0;
float acceleration=0.01;

void setup(){
  size(750,750);
  stroke(255);
}

void draw(){
  background (0);
  float angle=0.0;
  for (int offset = -10; offset < width+10; offset += 10) {
    for (int y = 1; y <= height; y += 3) {
      float x = offset + (sin(angle) * scaleVal);
      line(x, y, x, y+2);
      angle += angleInc;

      velocity += acceleration;
      y += velocity;
    }
    angle += PI;
  }
}

回答1:


Try using sin() to change the y position instead of x. The x position can simply increment.

The math may be daunting, but it gets fun once you get the hang of it. Imagine going around a circle with the radius of 1.0 in a cartesian coordinate system (0 is centre , x and y increase to the right and down and decrease towards left and top):

  • Let's say you start at the top, the highest value, the length radius of your circle (1.0).
  • As you decrease the angle, the x move to the left, but the y will go towards the centre( 0.0 )
  • then x will increase as it gets close to the centre and y will drop to bottom of the circle (-1.0)
  • then x will keep increasing until it reaches the right edge of the circle and the y value will increase and reach the vertical centre (0.0)
  • finally the x will decrease until it reaches the horizontal centre and y will increase and reach back to the top of the circle (1.0)

This image explains it pretty well:

Essentially it's like a converter: you plug in an angle from 0 to 360 degrees or TWO_PI radians (as sin works with angles in radians) and you get back a value between -1.0 and 1.0.

If you want to draw a sine wave, you have to draw multiple points:

  • the x position will increase value directly
  • the y position will increase the angle, but use the result of the sin() function to obtain a value that goes up and down.

The last thing to do is multiple the result of the sin() function by a larger number to essentially scale the sine wave (from -1.0 to 1.0) to a size more appropate for the screen.

Here's a quick commented demo you can use the mouse position to play with:

function setup(){
  createCanvas(640,100);
}
function draw(){
  background(255);
  
  var numberOfPoints = 1+(mouseX/2);
  //how often apart will the points be
  var widthPerPoint = width / numberOfPoints;
  //how much will the angle change from one point to another
  var anglePerPoint = TWO_PI/numberOfPoints;
  
  var waveHeight = 25;
  
  for(var i = 0; i < numberOfPoints; i++){
    var x = i *  widthPerPoint;
    var y = sin(anglePerPoint * i) * waveHeight;
    ellipse(x,50 + y,5,5);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>

The gist of it is this line:

var y = sin(anglePerPoint * i) * waveHeight;

which can be broken down to:

//increment the angle
var incrementedAngle = anglePerPoint * i;
//compute sine (-1.0,1.0)
var sine = sin(incrementedAngle);
//scale sine result
var waveY = sine * waveHeight;

Once you can draw a static sine wave, it's pretty easy to animate: to the angle increment at each point you add an increasing value. This increases the angle and essentially goes around the circle (TWO_PI) for you.

You can create your own variable to increase at your own rate or you can easily use an increasing value based on time(millis()) or frame(frameCount) which you can scale down (divide by a large number...or better yet multiple by a small fractional number):

function setup(){
  createCanvas(640,100);
}
function draw(){
  background(255);
  
  var numberOfPoints = 1+(mouseX/2);
  //how often apart will the points be
  var widthPerPoint = width / numberOfPoints;
  //how much will the angle change from one point to another
  var anglePerPoint = TWO_PI/numberOfPoints;
  
  var waveHeight = 25;
  
  for(var i = 0; i < numberOfPoints; i++){
    var x = i *  widthPerPoint;
    var y = sin(anglePerPoint * i + frameCount * 0.01) * waveHeight;
    ellipse(x,50 + y,5,5);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>

Hopefully the animation and simple demos above help illustrate the point. In even simpler terms, it's a bit of an illustion: you draw points that only move up and down, but each point use an increasing angle along the circle.

Have a look at Reuben Margolin's kinectic sculpture system demo:

(I recommend checking out the whole PopTech talk: it's inspiring)

You should have a look at the Processing SineWave example as well.

Here's a more complex encapsulating the notions in a resuable function to draw multiple waves to hint at an atmospheric perspective:

int numWaves = 5;
void setup(){
  size(400,400);
  noStroke();
}

void draw(){
   background(255);
   for(int i = 0 ; i < numWaves; i++){
     fill(30,120,180,map(i,0,numWaves-1,192,32));
     drawSineWave(HALF_PI,0.00025 * (i+1),50 + (10 * i),8,width,mouseY);
   }
   fill(255);
   text("drag mouse x to change number of waves",10,height-10);
}
/*
* radians   - how often does the wave cycle (larges values = more peaks)
* speed     - how fast is the wave moving
* amplitude - how high is the wave (from centre point)
* detail    - how many points are used to draw the wave (small=angled, many = smooth)
* y         - y centre of the wave
*/
void drawSineWave(float radians,float speed,float amplitude,int detail,float size,float y){
  beginShape();
  vertex(0,height);//fix to bottom
  //compute the distance between each point
  float xoffset = size / detail;
  //compute angle offset between each point
  float angleIncrement = radians / detail;
  //for each point
  for(int i = 0 ; i <= detail; i++){
    //compute x position
    float px = xoffset * i;
    //use sine function compute y
    //millis() * speed is like an ever increasing angle
    //to which we add the angle increment for each point (so the the angle changes as we traverse x
    //the result of sine is a value between -1.0 and 1.0 which we multiply to the amplitude (height of the wave)
    //finally add the y offset
    float py = y + (sin((millis() * speed) + angleIncrement * i) * amplitude);
    //add the point
    vertex(px,py);
  }
  vertex(size,height);//fix to bottom
  endShape();
}
void mouseDragged(){
  numWaves = 1+(int)mouseX/40;
}

Which you can also run bellow:

var numWaves = 5;
function setup(){
  createCanvas(400,400);
  noStroke();
}

function draw(){
   background(255);
   for(var i = 0 ; i < numWaves; i++){
     fill(30,120,180,map(i,0,numWaves-1,192,32));
     drawSineWave(HALF_PI,0.00025 * (i+1),50 + (10 * i),8,width,mouseY);
   }
   fill(255);
   text("drag mouse x to change number of waves",10,height-10);
}
/*
* radians   - how often does the wave cycle (larges values = more peaks)
* speed     - how fast is the wave moving
* amplitude - how high is the wave (from centre point)
* detail    - how many points are used to draw the wave (small=angled, many = smooth)
* y         - y centre of the wave
*/
function drawSineWave(radians,speed,amplitude,detail,size,y){
  beginShape();
  vertex(0,height);//fix to bottom
  //compute the distance between each point
  var xoffset = size / detail;
  var angleIncrement = radians / detail;
  for(var i = 0 ; i <= detail; i++){
    var px = xoffset * i;
    var py = y + (sin((millis() * speed) + angleIncrement * i) * amplitude);
    vertex(px,py);
  }
  vertex(size,height);//fix to bottom
  endShape();
}
function mouseDragged(){
  numWaves = ceil(mouseX/40);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script>

The only other suggestion I have, in terms of rendering, it to have play with beginShape(). Rather than having to worry about where to draw each line, simply pass a bunch of points(via vertex(x,y)) in between beginShape()/endShape() calls and let Processing connect the dots for you.




回答2:


Stack Overflow isn't really designed for general "how do I do this" type questions. It's for more specific "I tried X, expected Y, but got Z instead" type questions. That being said, I'll try to help in a general sense.

If you want to animate something going up and down, you have to modify its Y position over time.

One approach is to use the sin() or cos() functions to come up with a value that alternates between -1 and 1, which you can then multiply by a height and add to a center:

void setup() {
  size(100, 200);
}

void draw() {
  background (0);

  float centerY = height/2;
  float waveHeight = 75;
  float input = frameCount/10.0;
  float ballY = centerY+sin(input)*waveHeight;

  ellipse(width/2, ballY, 10, 10);
}

Another approach is to keep track of the position and speed yourself. When the position reaches a min or max, just reverse the speed. Something like this:

float ballY = 100;
float ySpeed = 1;

void setup() {
  size(100, 200);
}

void draw() {
  background (0);

  ballY += ySpeed;
  if(ballY < 0 || ballY > height){
    ySpeed *= -1;
  }

  ellipse(width/2, ballY, 10, 10);
}

You could also use the lerp() function. The point is that there are a million different ways to do this. The best thing you can do is to try something and post an MCVE if you get stuck. Good luck.



来源:https://stackoverflow.com/questions/40291618/animating-sin-waves-in-processing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!