Pass Slider value into a class

I am doing the NOC 5.3 Steering Behaviour coding challenge and try to map certain values like: maxSpeed, maxForce, and slowRadius to sliders. I also watched the slider tutorials by dan but struggle to implement this correctly as all examples just change something in the draw loop which seems easier.

Here is my p5.js link: 5.3 Steering Behavior + Sliders for Parameters help

sketch.js:

let slider;
let vehicle;

function setup() {
  createCanvas(400, 400);
  slider = createSlider(0,10,5);
  slider.position(1, 10);
  vehicle = new Vehicle(100, 100,slider.value());
}

function draw() {
  background(0);
  
  let target = createVector(mouseX, mouseY);
  fill(255, 0, 0);
  noStroke();
  ellipse(target.x, target.y, 32);

  let steering = vehicle.arrive(target);
  vehicle.applyForce(steering);
  vehicle.update();
  vehicle.show();
}

vehicle.js:

class Vehicle {
  constructor(x, y,speed) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = speed;
    this.maxForce = 0.4;
    this.r = 16;
  }


  evade(vehicle) {
    let pursuit = this.pursue(vehicle);
    pursuit.mult(-1);
    return pursuit;
  }

  pursue(vehicle) {
    let target = vehicle.pos.copy();
    let prediction = vehicle.vel.copy();
    prediction.mult(10);
    target.add(prediction);
    fill(0, 255, 0);
    circle(target.x, target.y, 16);
    return this.seek(target);
  }

  arrive(target) {
    // 2nd argument true enables the arrival behavior
    return this.seek(target, true);
  }

  flee(target) {
    return this.seek(target).mult(-1);
  }

  
  
  seek(target, arrival = false) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.maxSpeed;
    if (arrival) {
      let slowRadius = 50;
      let distance = force.mag();
      if (distance < slowRadius) {
      desiredSpeed = map(distance, 0, slowRadius, 0, this.maxSpeed);
    }
    }
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.maxForce);
    return force;
  }

  
  applyForce(force) {
    this.acc.add(force);
  }

  
  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    stroke(255);
    strokeWeight(2);
    fill(255);
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    circle(0, 0, this.r);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, this.r / 2, 0);
    pop();
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}

class Target extends Vehicle {
  constructor(x, y) {
    super(x, y);
    this.vel = p5.Vector.random2D();
    this.vel.mult(5);
  }

  show() {
    stroke(255);
    strokeWeight(2);
    fill(255);
    push();
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    circle(0, 0, this.r);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, this.r / 2, 0);
    pop();
  }
}

would appreciate it if someone could help. Thanks!

Hi @Tilman,

You can do something like this …
Used maxspeed as example.
You can just add other properties for your needs…

Cheers
— mnse

// let slider;
let vehicle;

function setup() {
	createCanvas(400, 400);
//	slider = createSlider(0, 10, 5);
//	slider.position(1, 10);
	vehicle = new Vehicle(100, 100, new Config());
}

function draw() {
	background(0);

	//let target = createVector(mouseX, mouseY);
	let target = createVector(min(mouseX, this.width), min(mouseY,this.height));
	fill(255, 0, 0);
	noStroke();
	ellipse(target.x, target.y, 32);

	let steering = vehicle.arrive(target);
	vehicle.applyForce(steering);
	vehicle.update();
	vehicle.show();
}

class Config {
	constructor() {
		this.maxSpeed = createSlider(0,10,5);
		this.maxSpeed.position(1,10);
	}
	
	getMaxSpeed() {
		return this.maxSpeed.value();
	}
}


class Vehicle {
	constructor(x, y, config) {
		this.pos = createVector(x, y);
		this.vel = createVector(0, 0);
		this.acc = createVector(0, 0);
		this.config = config;
		this.maxForce = 0.4;
		this.r = 16;
	}


	evade(vehicle) {
		let pursuit = this.pursue(vehicle);
		pursuit.mult(-1);
		return pursuit;
	}

	pursue(vehicle) {
		let target = vehicle.pos.copy();
		let prediction = vehicle.vel.copy();
		prediction.mult(10);
		target.add(prediction);
		fill(0, 255, 0);
		circle(target.x, target.y, 16);
		return this.seek(target);
	}

	arrive(target) {
		// 2nd argument true enables the arrival behavior
		return this.seek(target, true);
	}

	flee(target) {
		return this.seek(target).mult(-1);
	}



	seek(target, arrival = false) {
		let force = p5.Vector.sub(target, this.pos);
		let desiredSpeed = this.config.getMaxSpeed();
		if (arrival) {
			let slowRadius = 50;
			let distance = force.mag();
			if (distance < slowRadius) {
				desiredSpeed = map(distance, 0, slowRadius, 0, this.config.getMaxSpeed());
			}
		}
		force.setMag(desiredSpeed);
		force.sub(this.vel);
		force.limit(this.maxForce);
		return force;
	}


	applyForce(force) {
		this.acc.add(force);
	}


	update() {
		this.vel.add(this.acc);
		this.vel.limit(this.config.getMaxSpeed());
		this.pos.add(this.vel);
		this.acc.set(0, 0);
	}

	show() {
		stroke(255);
		strokeWeight(2);
		fill(255);
		push();
		translate(this.pos.x, this.pos.y);
		rotate(this.vel.heading());
		circle(0, 0, this.r);
		this.angle = this.vel.heading();
		stroke(0, 220, 220);
		strokeWeight(4);
		line(0, 0, this.r / 2, 0);
		pop();
	}

	edges() {
		if (this.pos.x > width + this.r) {
			this.pos.x = -this.r;
		} else if (this.pos.x < -this.r) {
			this.pos.x = width + this.r;
		}
		if (this.pos.y > height + this.r) {
			this.pos.y = -this.r;
		} else if (this.pos.y < -this.r) {
			this.pos.y = height + this.r;
		}
	}
}

class Target extends Vehicle {
	constructor(x, y) {
		super(x, y);
		this.vel = p5.Vector.random2D();
		this.vel.mult(5);
	}

	show() {
		stroke(255);
		strokeWeight(2);
		fill(255);
		push();
		translate(this.pos.x, this.pos.y);
		rotate(this.vel.heading());
		circle(0, 0, this.r);
		this.angle = this.vel.heading();
		stroke(0, 220, 220);
		strokeWeight(4);
		line(0, 0, this.r / 2, 0);
		pop();
	}
}

EDIT:

  • fix missed replacement for maxSpeed on map …
  • add boundary check for target.

Hey mnse,
thanks so much! Only had to add this.config.getMaxSpeed(); to the desired speed map function as well:
desiredSpeed = map(distance, 0, slowRadius, 0, this.maxSpeed); = desiredSpeed = map(distance, 0, slowRadius, 0, this.config.getMaxSpeed());

and now it´s totally working and I can add more sliders. perfect!

I only get a error message: "p5.Vector.prototype.mult: x, y, or z arguments are either undefined or not a finite number "

Yes! Sorry! Missed that one to replace. :slight_smile:

Where do you get this error. Tested it on webeditor but don’t get this message afteer fixing the issue above !?

Additionally you can also change this to stick on boundaries …

  let target = createVector(min(mouseX, this.width), min(mouseY,this.height));    

Great addition. Thanks! The error message magically disappeared…

I´d have 1 more question :slight_smile:
How can I make the slider send out a float number range like
this.maxForce = createSlider(0.1,1,0.5);

Do I have to divide maxForce or is there a better way of doing this?

Hi @Tilman,

Guess the former floating point issues are already fixed.

Try this…

this.maxForce = createSlider(0.1,1.0,0.5,0.1);

If not fixed, use this and on the getter just divide by 10.

this.maxForce = createSlider(1,10,5);

Cheers
— mnse

Thank you!
Kind of completed the task to map all important parameters as sliders.
Only thing I am struggeling is the “slowRadius” I have a slider assigned but I wished I could represent it visually on the target. However the target is not a class. If I try to add this.slowRadius to the radius of the target circle I get errors.

let vehicle;
function setup() {
  createCanvas(600, 600);
  vehicle = new Vehicle(100, 100, new Speed(), new Force(), new Slowradius());
}

function draw() {
  background(50);
noStroke(0);
  text("max speed", +150, 25);
  text("max force", +150, 50);
  text("slowRadius", +150, 75);
  
let target = createVector(min(mouseX, this.width), min(mouseY,this.height));

  fill(255, 0, 0);
  noStroke();
  ellipse(target.x, target.y, 32);
stroke(255);
fill(50, 0, 50, 50);
circle(target.x, target.y, 200);



  let steering = vehicle.arrive(target);
  vehicle.applyForce(steering);
  vehicle.update();
  vehicle.show();
  //target.show();
}

class Speed {
  constructor() {
    this.maxSpeed = createSlider(0, 30, 15);
    this.maxSpeed.position(1, 10);
  }
  getMaxSpeed() {
    return this.maxSpeed.value();
  }
}

class Force {
  constructor() {
    this.maxForce = createSlider(1, 40, 20);
    this.maxForce.position(1, 35);
  }
  getMaxForce() {
    return this.maxForce.value();
  }
}

class Slowradius {
  constructor() {
    this.slowRadius = createSlider(1, 200, 100);
    this.slowRadius.position(1, 60);
  }
  getMaxSlowradius() {
    return this.slowRadius.value();
  }
}
class Vehicle {
  constructor(x, y, speed, maxforce, slowradius) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.speed = speed;
    this.maxForce = maxforce;
    this.slowRadius = slowradius;
    this.r = 16;
  }

  evade(vehicle) {
    let pursuit = this.pursue(vehicle);
    pursuit.mult(-1);
    return pursuit;
  }

  pursue(vehicle) {
    let target = vehicle.pos.copy();
    let prediction = vehicle.vel.copy();
    prediction.mult(10);
    target.add(prediction);
    fill(0, 255, 0);
    circle(target.x, target.y, 16);
    return this.seek(target);
  }

  arrive(target) {
    // 2nd argument true enables the arrival behavior
    return this.seek(target, true);
  }

  flee(target) {
    return this.seek(target).mult(-1);
  }

  seek(target, arrival = false) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.speed.getMaxSpeed();
    if (arrival) {
      let slowRadius = this.slowRadius.getMaxSlowradius();
      let distance = force.mag();
      if (distance < slowRadius) {
        desiredSpeed = map(
          distance,
          0,
          slowRadius,
          0,
          this.speed.getMaxSpeed()
        );
      }
    }
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.maxForce.getMaxForce() * 0.1);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.speed.getMaxSpeed());
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    stroke(255);
    strokeWeight(2);
    fill(255);
    push();

    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());

    circle(0, 0, this.r * 2);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, (this.r * 2) / 2, 0);

    pop();
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}

class Target {
  constructor(x, y) {
    //super(x, y);
    this.vel = p5.Vector.random2D();
    this.vel.mult(5);
  }

  show() {
    push();
    stroke(255);
    strokeWeight(2);
    fill(255, 0, 255);
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    circle(0, 0, 100);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, this.r / 2, 0);
    fill(255);
    pop();
  }
}

Hi @Tilman,

Why for every Parameter a separate class?
Wouldn’t it be easier to put all in the Config class from my example !?

Regarding the slowdown radius you can use the Parameter from vehicle object…

  fill(50, 0, 50, 50);
  ellipse(target.x, target.y, 
  vehicle.slowRadius.getMaxSlowradius());

Cheers
— mnse

1 Like

Hey @mnse thanks so much! I did redo eveything as you suggested and the parameters are all in one class. But this still leaves me with one problem. I can´t get the “slow radius” parameter to work for drawing the ellipse. I tried your suggestion:

fill(50, 0, 50, 50);
  ellipse(target.x, target.y, 
  vehicle.slowRadius.getMaxSlowradius());

but get an error message… I guess I have to declare some variable or something to get the value from the class into drawing the ellipse? It has to do with the order where I do what, correct?

sketch.js

let vehicle;
function setup() {
  createCanvas(800, 800);
  vehicle = new Vehicle(100, 100, new Config());
}

function draw() {
  background(20, 200, 100);
  noStroke(0);
  text("max speed", +150, 25);
  text("max force", +150, 50);
  text("slowRadius", +150, 75);

  let listener = createVector(width / 2, height / 2);
  let target = createVector(min(mouseX, this.width), min(mouseY, this.height));

  fill(255, 0, 0);
  noStroke();
  ellipse(target.x, target.y, 32);
  noStroke();
  fill(50, 0, 50, 20);
  ellipse(target.x, target.y, vehicle.slowRadius.getMaxSlowradius());
  //How do I get the slow radius from the config class here?

  
  let steering = vehicle.arrive(target);
  vehicle.applyForce(steering);
  vehicle.distancevalue(listener);
  vehicle.listenradius(listener);
  vehicle.update();
  vehicle.show();
  //target.show();
}


//SLIDERS
class Config {
  constructor() {
    this.maxSpeed = createSlider(0, 10, 5);
    this.maxSpeed.position(1, 10);

    this.maxForce = createSlider(1, 20, 5);
    this.maxForce.position(1, 35);

    this.slowRadius = createSlider(1, 500, 100);
    this.slowRadius.position(1, 60);
  }
  getMaxSpeed() {
    return this.maxSpeed.value();
  }
  getMaxForce() {
    return this.maxForce.value();
  }

  getMaxslowRadius() {
    return this.slowRadius.value();
  }
}

vehicle.js

class Vehicle {
  constructor(x, y, config) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.config = config;
    this.r = 16;
  }

  arrive(target) {
    // 2nd argument true enables the arrival behavior
    return this.seek(target, true);
  }

  flee(target) {
    return this.seek(target).mult(-1);
  }

  seek(target, arrival = false) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.config.getMaxSpeed();
    if (arrival) {
      let slowRadius = this.config.getMaxslowRadius();
      let distance = force.mag();
      if (distance < slowRadius) {
        desiredSpeed = map(distance,0,slowRadius,0,this.config.getMaxSpeed());
        
      }
    }
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.config.getMaxForce() * 0.1);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.config.getMaxSpeed());
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    noStroke();
    //strokeWeight(2);
    fill(0, 0, 255);
    push();

    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());

    circle(0, 0, this.r * 2);
    
    //vehicle heading line
    this.angle = this.vel.heading();
    stroke(255);
    strokeWeight(4);
    line(0, 0, (this.r * 2) / 2, 0);

    pop();
  }

  listenradius(listener) {
    push();
    
    //draw listener
    translate(listener.x, listener.y);
    fill(200, 0, 200, 100);
    circle(0, 0, this.r * 4);
    
    
    let postolistener = p5.Vector.sub(this.pos, listener);
    let listenerup = createVector(0, -this.r * 2);
    fill(10);
    strokeWeight(1);
    stroke(1);
    line(0, 0, listenerup.x, listenerup.y);
    //line(0, 0, postolistener.x, postolistener.y);
    
    //calculate angle between vehicle and listener
    let angleBetween = postolistener.angleBetween(listenerup);
    pop(); 
    text("ANGLE TO LISTENER " + angleBetween.toFixed(2) + " radians",20,180,300,55);
  }

  distancevalue(listener) {
    let distvalue = p5.Vector.sub(listener, this.pos);
    strokeWeight(0);
    fill(255, 255, 0);
    textSize(16);
    textStyle(NORMAL);
    text("DISTANCE TO LISTENER  " + floor(distvalue.mag()), 20, 120, 300, 55);
    text("DISTANCE X TO LISTENER  " + floor(distvalue.x), 20, 140, 300, 55);
    text("DISTANCE Y TO LISTENER  " + floor(distvalue.y), 20, 160, 300, 55);
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}

class Target {
  constructor(x, y) {
    this.vel = p5.Vector.random2D();
    this.vel.mult(5);
  }

  show() {
    push();
    stroke(255);
    strokeWeight(2);
    fill(255, 0, 255);
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    circle(0, 0, 100);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, this.r / 2, 0);
    fill(255);
    pop();

  }
}

Bildschirmfoto 2022-09-20 um 16.34.36

Hi @Tilman,

Try

ellipse(target.x, target.y, vehicle.config.getMaxSlowradius());

Cheers
— mnse

@mnse thanks! I tried but get this error message:
I tried. It gives an Error:
TypeError: vehicle.config.slowRadius.getMaxSlowradius is not a function.

Hi @Tilman,

Look exact …

@mnse
I did exactly copy paste it. but get this error:
“TypeError: vehicle.config.getMaxSlowradius is not a function. (In ‘vehicle.config.getMaxSlowradius()’, ‘vehicle.config.getMaxSlowradius’ is undefined)”

sketch.js

let vehicle;
function setup() {
  createCanvas(800, 800);
  vehicle = new Vehicle(100, 100, new Config());
}

function draw() {
  background(20, 200, 100);
  noStroke(0);
  text("max speed", +150, 25);
  text("max force", +150, 50);
  text("slowRadius", +150, 75);

  let listener = createVector(width / 2, height / 2);
  let target = createVector(min(mouseX, this.width), min(mouseY, this.height));

  fill(255, 0, 0);
  noStroke();
  ellipse(target.x, target.y, 32);
  noStroke();
  fill(50, 0, 50, 20);
  ellipse(target.x, target.y, vehicle.config.getMaxSlowradius());
  //How do I get the slow radius from the config class here?

  
  let steering = vehicle.arrive(target);
  vehicle.applyForce(steering);
  vehicle.distancevalue(listener);
  vehicle.listenradius(listener);
  vehicle.update();
  vehicle.show();
  //target.show();
}


//SLIDERS
class Config {
  constructor() {
    this.maxSpeed = createSlider(0, 10, 10);
    this.maxSpeed.position(1, 10);

    this.maxForce = createSlider(1, 20, 5);
    this.maxForce.position(1, 35);

    this.slowRadius = createSlider(1, 500, 100);
    this.slowRadius.position(1, 60);
  }
  getMaxSpeed() {
    return this.maxSpeed.value();
  }
  getMaxForce() {
    return this.maxForce.value();
  }

  getMaxslowRadius() {
    return this.slowRadius.value();
  }
}

vehicle.js

class Vehicle {
  constructor(x, y, config) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.config = config;
    this.r = 16;
  }

  arrive(target) {
    // 2nd argument true enables the arrival behavior
    return this.seek(target, true);
  }

  flee(target) {
    return this.seek(target).mult(-1);
  }

  seek(target, arrival = false) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.config.getMaxSpeed();
    if (arrival) {
      let slowRadius = this.config.getMaxslowRadius();
      let distance = force.mag();
      if (distance < slowRadius) {
        desiredSpeed = map(distance,0,slowRadius,0,this.config.getMaxSpeed());
        
      }
    }
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.config.getMaxForce() * 0.1);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.config.getMaxSpeed());
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    noStroke();
    //strokeWeight(2);
    fill(0, 0, 255);
    push();

    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());

    circle(0, 0, this.r * 2);
    
    //vehicle heading line
    this.angle = this.vel.heading();
    stroke(255);
    strokeWeight(4);
    line(0, 0, (this.r * 2) / 2, 0);

    pop();
  }

  listenradius(listener) {
    push();
    
    //draw listener
    translate(listener.x, listener.y);
    fill(200, 0, 200, 100);
    circle(0, 0, this.r * 4);
    
    
    let postolistener = p5.Vector.sub(this.pos, listener);
    let listenerup = createVector(0, -this.r * 2);
    fill(10);
    strokeWeight(1);
    stroke(1);
    line(0, 0, listenerup.x, listenerup.y);
    line(0, 0, postolistener.x, postolistener.y);
    
    //calculate angle between vehicle and listener
    let angleBetween = postolistener.angleBetween(listenerup);
    pop(); 
    text("ANGLE TO LISTENER " + angleBetween.toFixed(2) + " radians",20,180,300,55);
  }

  distancevalue(listener) {
    let distvalue = p5.Vector.sub(listener, this.pos);
    strokeWeight(0);
    fill(255, 255, 0);
    textSize(16);
    textStyle(NORMAL);
    text("DISTANCE TO LISTENER  " + floor(distvalue.mag()), 20, 120, 300, 55);
    text("DISTANCE X TO LISTENER  " + floor(distvalue.x), 20, 140, 300, 55);
    text("DISTANCE Y TO LISTENER  " + floor(distvalue.y), 20, 160, 300, 55);
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}

class Target {
  constructor(x, y) {
    this.vel = p5.Vector.random2D();
    this.vel.mult(5);
  }

  show() {
    push();
    stroke(255);
    strokeWeight(2);
    fill(255, 0, 255);
    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());
    circle(0, 0, 100);
    this.angle = this.vel.heading();
    stroke(0, 220, 220);
    strokeWeight(4);
    line(0, 0, this.r / 2, 0);
    fill(255);
    pop();

  }
}

I was able to run your code when all the typos were fixed. P5.js is case sensitive and you must be consistent: use something like this throughout and it should work:

let slowRadius = this.config.getMaxSlowRadius()

MaxSlowRadius must be the same case every time.

2 Likes

Hi,

Sorry for the typo…

Thanks @svan for correction.

Cheers
— mnse

1 Like

Sorry I keep coming back! :slight_smile:
@mnse @svan

I checked for all title case issues and the code runs. It is only that I can´t get the config.getMaxSlowRadius(); value into the the ellipse I draw in function draw()

  ellipse(target.x, target.y, 100);
  //How do I get the slow radius from the config class here?

That is the section of the code. I just typed in 100 but there is where the config.getMaxSlowRadius(); should go.

I also tried it with your suggestion typing let slowRadius = this.config.getMaxSlowRadius()
but could not get it to work… I get the value passed over to the class Vehicle but not into funtion draw()

sketch.js

let vehicle;

function setup() {
  createCanvas(800, 800);
  vehicle = new Vehicle(100, 100, new Config());
}

function draw() {
  background(20, 200, 100);
  noStroke(0);
  text("max speed", +150, 25);
  text("max force", +150, 50);
  text("slowRadius", +150, 75);

  let listener = createVector(width / 2, height / 2);
  let target = createVector(min(mouseX, this.width), min(mouseY, this.height));

  fill(255, 0, 0);
  noStroke();
  ellipse(target.x, target.y, 32);
  noStroke();
  fill(50, 0, 50, 20);
  ellipse(target.x, target.y, 100);
  //How do I get the slow radius from the config class here?

  
  let steering = vehicle.arrive(target);
  vehicle.applyForce(steering);
  vehicle.distancevalue(listener);
  vehicle.listenradius(listener);
  vehicle.update();
  vehicle.show();
  //target.show();
}


//SLIDERS
class Config {
  constructor() {
    this.maxSpeed = createSlider(0, 10, 10);
    this.maxSpeed.position(1, 10);

    this.maxForce = createSlider(1, 20, 5);
    this.maxForce.position(1, 35);

    this.slowRadius = createSlider(1, 500, 100);
    this.slowRadius.position(1, 60);
  }
  getMaxSpeed() {
    return this.maxSpeed.value();
  }
  getMaxForce() {
    return this.maxForce.value();
  }

  getMaxSlowRadius() {
    return this.slowRadius.value();
  }
}

vehicle.js

class Vehicle {
  constructor(x, y, config) {
    this.pos = createVector(x, y);
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.config = config;
    this.r = 16;
  }

  arrive(target) {
    // 2nd argument true enables the arrival behavior
    return this.seek(target, true);
  }

  flee(target) {
    return this.seek(target).mult(-1);
  }

  seek(target, arrival = false) {
    let force = p5.Vector.sub(target, this.pos);
    let desiredSpeed = this.config.getMaxSpeed();
    if (arrival) {
      let slowRadius = this.config.getMaxSlowRadius();
      let distance = force.mag();
      if (distance < slowRadius) {
        desiredSpeed = map(
          distance,
          0,
          slowRadius,
          0,
          this.config.getMaxSpeed()
        );
      }
    }
    force.setMag(desiredSpeed);
    force.sub(this.vel);
    force.limit(this.config.getMaxForce() * 0.1);
    return force;
  }

  applyForce(force) {
    this.acc.add(force);
  }

  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.config.getMaxSpeed());
    this.pos.add(this.vel);
    this.acc.set(0, 0);
  }

  show() {
    noStroke();
    //strokeWeight(2);
    fill(0, 0, 255);
    push();

    translate(this.pos.x, this.pos.y);
    rotate(this.vel.heading());

    circle(0, 0, this.r * 2);

    //vehicle heading line
    this.angle = this.vel.heading();
    stroke(255);
    strokeWeight(4);
    line(0, 0, (this.r * 2) / 2, 0);

    pop();
  }

  listenradius(listener) {
    push();
    //draw listener
    translate(listener.x, listener.y);
    fill(200, 0, 200, 100);
    circle(0, 0, this.r * 4);

    let postolistener = p5.Vector.sub(this.pos, listener);
    let postolistener2 = p5.Vector.sub(listener, this.pos);
    let listenerup = createVector(0, -this.r * 2);
    fill(10);
    strokeWeight(1);
    stroke(1);
    line(0, 0, listenerup.x, listenerup.y);
    //line(0, 0, postolistener.x, postolistener.y);

    //calculate angle between vehicle and listener
    let angleBetween = postolistener.angleBetween(listenerup);

    pop();
    text(
      "ANGLE TO LISTENER " + angleBetween.toFixed(2) + " radians",
      20,
      180,
      300,
      55
    );

    let angleBetween2 = postolistener2.angleBetween(this.vel);

    //print(angleBetween2);
    text(
      "ANGLE TO LISTENER " + angleBetween2.toFixed(2) + " radians",
      20,
      200,
      300,
      55
    );
  }

  distancevalue(listener) {
    let distvalue = p5.Vector.sub(listener, this.pos);
    strokeWeight(0);
    fill(255, 255, 0);
    textSize(16);
    textStyle(NORMAL);
    text("DISTANCE TO LISTENER  " + floor(distvalue.mag()), 20, 120, 300, 55);
    text("DISTANCE X TO LISTENER  " + floor(distvalue.x), 20, 140, 300, 55);
    text("DISTANCE Y TO LISTENER  " + floor(distvalue.y), 20, 160, 300, 55);
  }

  edges() {
    if (this.pos.x > width + this.r) {
      this.pos.x = -this.r;
    } else if (this.pos.x < -this.r) {
      this.pos.x = width + this.r;
    }
    if (this.pos.y > height + this.r) {
      this.pos.y = -this.r;
    } else if (this.pos.y < -this.r) {
      this.pos.y = height + this.r;
    }
  }
}

Hi @Tilman,

I’ve copied your code again, and inserted as suggested above …

  ellipse(target.x, target.y, vehicle.config.getMaxSlowRadius());

And this is working and shows the circle of getMaxSlowRadius…

Here is the working example

Cheers
— mnse

1 Like

@mnse Thank you! Don´t know what got confused before but now it´s working here too.
Thanks again for all the help @mnse @svan

@mnse
I incorporated your (great working!) Config class for parameters in another sketch. It seems to work.
But in that patch, one places attractors with a mouse click. This interferes with the slider… it is not really slidable. But due altering it´s default value I could check it passes the value.

I tried to place the sliders outside the canvas but this still interferes and the slider is not operational.
Is there a way to place the slider in a section that does not interfere with the canvas functions?

Cheers!
Tilman

sketch.js

var fr;

var inc = 0.04;
var scl = 20;
var cols, rows;

var zoff = 0;

var start = 0;

//OpenSimplexNoise
//let noise;

var flowfield;
var attractors = [];
let particles = [];
var attractorsKind = []; // true or false

// a var to track elapsed time
let elapsed = 0;
// one for how many particles
//let particles_to_release = particle.config.getParticleNumber();
let particles_to_release = 1;
// and one for interval to release
// 1000 = 1seg (milliseconds)
let interval = 500;

function setup() {
  createCanvas(800, 800);
  
  
  pixelDensity(1);
  
  //OpenSimplexNoise
  //noise = new OpenSimplexNoise(Date.now());

  cols = floor(width / scl);
  rows = floor(height / scl);
  fr = createP("");

  //flowfield array
  flowfield = new Array(cols * rows);
  
  //all set, lets reset our timer
  elapsed = millis();
}


function mousePressed() {
  if (mouseX < width && mouseY < height){
  attractors.push(createVector(mouseX,mouseY));
    }else{
    //do nothing  
 }     
}

function draw() {
  background(20);
  stroke(220, 200, 0);
  fill(220, 200, 0,100)
  strokeWeight(2);
  //noFill();
  ellipse(400,400,100)
  
  strokeWeight(20);
  for (var i = 0; i < attractors.length; i++) {
    if (attractorsKind[i]) {
      stroke(0, 255, 0);
    } else {
      stroke(255, 0, 0);
    }
    point(min(attractors[i].x,width), min(attractors[i].y,height));
  }
  
  if (millis() - elapsed > interval) {
    for (let i = 0; i < particles_to_release; i++) {
      let p = new Particle(400,400, new Config());
      particles.push(p);
    }
    
   
    
    //reset de clock...
    //voila :)
    elapsed = millis();
  }
   

  var yoff = 0;

  for (var y = 0; y < rows; y++) {
    var xoff = 0;
    for (var x = 0; x < cols; x++) {
      var index = x + y * cols;

      //simplex noise
      //var n = noise.noise3D(xoff, yoff, zoff);
      //var angle = map(n, -1,1,0,TWO_PI*4);
      //Perlin Noise
      var angle = noise(xoff, yoff, zoff) * TWO_PI * 4;
      var v = p5.Vector.fromAngle(angle);
      v.setMag(0.4);

      flowfield[index] = v;

      xoff += inc;
      strokeWeight(2);
      stroke(120, 20, 200);
      push();
      translate(x * scl, y * scl);
      rotate(v.heading());
      line(0, 0, scl, 0);
      pop();
    }
    yoff += inc;

    zoff += 0.0008;
  }
  for (var i = particles.length - 1; i >= 0; i--) {
    for (var j = 0; j < attractors.length; j++) {
      particles[i].attracted(attractors[j], j, attractorsKind[j]);
    }
    particles[i].follow(flowfield);
    particles[i].update();
    particles[i].show();
    //particles[i].edges();
    if (particles[i].finished()) {
    //remove this particle
    particles.splice(i, 1);
    }
  }
  }
  function mousePressed() {
  attractors.push(createVector(mouseX, mouseY));
  attractorsKind.push(mouseButton === LEFT); // true or false

}

//}
class Config {
  constructor() {
    this.maxSpeed = createSlider(0, 20, 10);
    this.maxSpeed.position(1, 900);


  }
  getMaxSpeed() {
    return this.maxSpeed.value();
}

}

particle.js

function Particle(x, y, config) {
  this.x =x;
  this.y =y;

  this.pos = createVector(x+random(-50,50),y+random(-50,50));
  this.vel = createVector();
  this.acc = createVector();
  this.config = config;

  this.maxspeed = this.config.getMaxSpeed();;
  this.alpha = 255;

 
  this.particleinterval = 2500;

  //all set, lets reset our timer
  this.particleelapsed = millis();
   
  // when alpha less than 0 function is true 
  //for particle delete
  this.finished = function () {
  return this.alpha < 0;
 } 

  this.update = function () {
    this.vel.add(this.acc);
    this.vel.limit(this.maxspeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
    
     if (millis() - this.particleelapsed > this.particleinterval) {
      this.alpha -= 255;
      this.particleelapsed = millis();
    }
  }
  
  
  this.follow = function(vectors) {
    var x = floor(this.pos.x / scl);
    var y = floor(this.pos.y / scl);
    var index = x +y * cols;
    var force = vectors[index];
    this.applyForce(force);
  }
  
  
  this.applyForce = function (force) {
    this.acc.add(force);
  };
  
  
   this.attracted = function (target, j, kind) {
    //var dir = target -this.pos
    var force = p5.Vector.sub(target, this.pos);
    var dsquared = force.magSq();
    dsquared = constrain(dsquared, 5, 50);
    var G = 20;
    var strength = G/dsquared;
    force.setMag(strength);
    
    if (kind) {
      force.mult(1);
    } else {
      force.mult(-1);
    }
    this.acc.add(force);
  }
  
  this.show = function () {
    noStroke();
    fill(220, 200, 0, this.alpha);
    circle(this.pos.x, this.pos.y, 10);
  };

  
  this.edges = function () {
    if (this.pos.x > width) this.pos.x = 0;
    if (this.pos.x < 0) this.pos.x = width;
    if (this.pos.y > height) this.pos.y = 0;
    if (this.pos.y < 0) this.pos.y = height;
  };
}

Hi @Tilman,

You need to be a bit more thoughtfull with the code you are creating…
Always try to imagine what happens beforehand. (not always trial and error).

The current situation is, that you always add an additional new slider at the same position when adding a new particle…

What can you do to solve the issue?
Keep the config once (ie created in setup) and use it on the particles or in a global manner.

Cheers
— mnse