Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a sliding member in Processing with particle systems

I am simulating particle system in Processing. Based on Daniel Shiffman’s Nature of Code book, I did a spring and then I started experimenting with sliders to do one that has longer or shorter length based on a slider.

Now, I am trying to make one that slides by the slider, the two particles move to the same direction of the two particles. I did it with the PVector add, finding the new position and drawing the node, but it doesn’t work when I have multiple members and one is affected by the others. I need to apply a force to do this: see applyForce() function.

void update(float distance) {
  PVector force = PVector.sub(b.location, a.location); 
  float d = force.mag();
  float x = d - distance;
 //direction of the force
  force.normalize();
  force.mult(-1 * k* x/mass);
 //apply to one node
  b.applyForce(force); 
  force.mult(-1);
 //apply opposite to the other node
  a.applyForce(force);
}

//Newton's law: F = M * A
void applyForce(PVector force) {
  PVector f = force.get();
  f.div(mass);
  acceleration.add(f);
}

Check the diagram below:

diagram

(a) is what I want to have, (b) is how it's doing it now.

In the first example the length is the same and the members slides (both particles).

In the second the length is bigger and does not slide

Please let me know if you know how to apply a force that slides the member.

Thank you

like image 492
Apollon1954 Avatar asked Aug 20 '15 22:08

Apollon1954


1 Answers

If I understood correctly, you're trying to do a few things:

  1. change the spring's length
  2. translate the spring's endpoints in the spring's direction
  3. control the above parameters using sliders

The first part is trivial since the Spring object has a len property. The second involves a bit of vector math:

  1. the direction of a line is the subtraction of it's two end points
  2. a vector can be scaled easily to any length by normalising it first (reducing it so it's length is equal to 1.0) and then multiplying by a scalar value.
  3. A vector can be translated by simply adding another vector to itself

Here is a commented sketch implementing the points above:

//sliders to control spring rest length and translation
Slider rlength = new Slider("rest length", 5, 5, 200, 20, 50, 250, 100, false);
Slider translate = new Slider("translate", 5, 30, 200, 20, -10, 10, 0, false);

Spring spring = new Spring(new Bob(75,350),new Bob(350,75),(int)rlength.value);

void setup(){
  size(400,400);
  spring.k = 0.01;//tweak elasticity
}
void draw(){
  //  update
  //update sliders
  rlength.update(mouseX,mouseY,mousePressed);
  translate.update(mouseX,mouseY,mousePressed);
  //update spring
  spring.a.update();
  spring.b.update();
  spring.update();
  //make both points draggable
  spring.a.drag(mouseX, mouseY);
  spring.b.drag(mouseX, mouseY);
  //draw
  background(255);
  rlength.draw();
  translate.draw();
  spring.display();
}
//handle mouse events for spring points dragging
void mousePressed() {
  spring.a.clicked(mouseX, mouseY);
  spring.b.clicked(mouseX, mouseY);
}
void mouseReleased() {
  spring.a.stopDragging();
  spring.b.stopDragging();
}
//handle slider events
void onSliderUpdate(Slider s){
  if(s == rlength) spring.len = rlength.value;
  if(s == translate){
    //compute the direction of the spring by subtracting the two points
    PVector direction = PVector.sub(spring.a.location,spring.b.location);
    //normalize the vector -> it will not have a length/magnitude of 1.0, but will still point in the line direction
    direction.normalize();
    //scale or multiply the normalized vector to the translation amount
    direction.mult(translate.value);
    //finally, add the result to each spring point, essentially offsetting/translating
    spring.a.location.add(direction);
    spring.b.location.add(direction);
  } 
}
//Slider
class GUIElement{
  float w,h,x,y;//width, height and position
  color bg = color(200);//background colour
  color fg = color(0);//foreground colour
  String label;
  GUIElement(String label,float x,float y,float w,float h){
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.label = label;
  }
  void update(int mx,int my,boolean md){}
  void draw(){}
}
class Slider extends GUIElement{
  float min,max,value,pvalue;//slider values: minimum, maximum and current
  float cx,pw = 20;//current slider picker position, picker width

  boolean updating,liveDrag = true,isInt = false;
  //label to display on slider, it's position(x,y), size(w,h) and values(min, max and default/current)
  Slider(String label,float x,float y,float w,float h,float min,float max,float value,boolean isInt){
    super(label,x,y,w,h);
    this.min = min;
    this.max = max;
    this.value = value;
    this.isInt = isInt;
    cx = map(value,min,max,x,x+w);
  }
  void update(int mx,int my,boolean md){
    if(md){
      if((mx >= x && mx <= (x+w)) &&
         (my >= y && my <= (y+h))){
        cx = mx;
        value = map(cx,x,x+w,min,max);
        updating = true;
        if(liveDrag){
          boolean updated = (isInt ? ((int)value != (int)pvalue) : (value != pvalue));
          if(updated){
            pvalue = value;
            onSliderUpdate(this);
          }
        }
      }else updating = false;
    }else{
      if(updating){
        updating = false;
        onSliderUpdate(this);
      }  
    }
  }
  void draw(){
    pushStyle();
    noStroke();
    fill(bg);
    rect(x,y,w,h);
    fill(fg,64);
    rect(x,y,cx-x,h);//this displays a rect that stretches based on the value
    fill(0);
    text(label+": "+(isInt ? (int)value : value),x+pw,y+h*.75);
    popStyle();
  }
  String toString(){
    return label + ":" + value;
  }
}

// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com

// Bob class, just like our regular Mover (location, velocity, acceleration, mass)

class Bob { 
  PVector location;
  PVector velocity;
  PVector acceleration;
  float mass = 12;

  // Arbitrary damping to simulate friction / drag 
  float damping = 0.95;

  // For mouse interaction
  PVector dragOffset;
  boolean dragging = false;

  // Constructor
  Bob(float x, float y) {
    location = new PVector(x,y);
    velocity = new PVector();
    acceleration = new PVector();
    dragOffset = new PVector();
  } 

  // Standard Euler integration
  void update() { 
    velocity.add(acceleration);
    velocity.mult(damping);
    location.add(velocity);
    acceleration.mult(0);
  }

  // Newton's law: F = M * A
  void applyForce(PVector force) {
    PVector f = force.get();
    f.div(mass);
    acceleration.add(f);
  }


  // Draw the bob
  void display() { 
    stroke(0);
    strokeWeight(2);
    fill(175);
    if (dragging) {
      fill(50);
    }
    ellipse(location.x,location.y,mass*2,mass*2);
  } 

  // The methods below are for mouse interaction

  // This checks to see if we clicked on the mover
  void clicked(int mx, int my) {
    float d = dist(mx,my,location.x,location.y);
    if (d < mass) {
      dragging = true;
      dragOffset.x = location.x-mx;
      dragOffset.y = location.y-my;
    }
  }

  void stopDragging() {
    dragging = false;
  }

  void drag(int mx, int my) {
    if (dragging) {
      location.x = mx + dragOffset.x;
      location.y = my + dragOffset.y;
    }
  }
}

// Nature of Code 2011
// Daniel Shiffman
// Chapter 3: Oscillation

// Class to describe an anchor point that can connect to "Bob" objects via a spring
// Thank you: http://www.myphysicslab.com/spring2d.html

class Spring { 

  // Location
  PVector anchor;

  // Rest length and spring constant
  float len;
  float k = 0.2;

  Bob a;
  Bob b;

  // Constructor
  Spring(Bob a_, Bob b_, int l) {
    a = a_;
    b = b_;
    len = l;
  } 

  // Calculate spring force
  void update() {
    // Vector pointing from anchor to bob location
    PVector force = PVector.sub(a.location, b.location);
    // What is distance
    float d = force.mag();
    // Stretch is difference between current distance and rest length
    float stretch = d - len;

    // Calculate force according to Hooke's Law
    // F = k * stretch
    force.normalize();
    force.mult(-1 * k * stretch);
    a.applyForce(force);
    force.mult(-1);
    b.applyForce(force);
  }


  void display() {
    strokeWeight(3);
    stroke(0);
    line(a.location.x, a.location.y, b.location.x, b.location.y);
    ellipse(a.location.x, a.location.y,10,10);
    ellipse(b.location.x, b.location.y,10,10);
  }
}

Slider controlled Spring

like image 74
George Profenza Avatar answered Sep 23 '22 20:09

George Profenza