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:
(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
If I understood correctly, you're trying to do a few things:
The first part is trivial since the Spring object has a len
property.
The second involves a bit of vector math:
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With