xxxxxxxxxx
202
var roadlength = 1000;
var roadpos_y = 50;
// Typical driving speed is 50 km/h which is close to 14 m/s. Here a comfortable speed
// to look at is 2-3 pixels per frame so...
// s_p = k (pixels/metre) * s_m
// t_f = l (frames/s) * t_s
// v = s_p / t_f = s_m / t_s * k/l
// a = s_p / t_f^2 = s_m / t_s^2 k/l^2
// car length = 4.5 m * k = 24 pixels => k = 5.3 pixels/m
// v = 3 pixels/frame = k/l * 14 m/s => l = 5.3*14/3 frames/s = 24.7 frames/s
// length conversion: 5 pixels/m
// speed conversion: 1/5 pixels/m * s/frames
// acceleration conversion: 1/125 pixels/m * (s/frames)^2
var cars = [];
var TrafficLight = function() {
// Normal lights switch every 20 seconds or so, which makes for 20
this.position = 950;
this.period = 20000; // In milliseconds
this.buffer = 2000; // Buffer between a red light on one road and a green on the one crossing it.
this.offset = 0; // Offset from the normal timing.
}
TrafficLight.prototype.isOn = function() {
remainder = millis() % this.period;
// |- green -------------|- bfr -|- red ---------------|- bfr -|
begin = this.offset;
end = this.period/2-this.buffer+this.offset;
// console.log("rem: "+remainder + " - ("+begin+','+end+") constr: "+ constrain(remainder, this.offset, this.period/2-this.buffer+this.offset));
return remainder == constrain(remainder, begin, end);
}
function setup() {
car_size = 12;
pixels_per_metre = car_size/4.5; // pixels/metres
min_separation = (4.5+2)*pixels_per_metre; // 4.5 m car length + 2 m seperation
fps = 24;
// Normal speed is 50 km/h = 50 000 m * pixels_per_metre / (3600 s * fps) =
speed_conversion = 1000*pixels_per_metre/(3600*fps);
normal_speed = 50*speed_conversion;
max_speed = 60*speed_conversion;
// Normal acceleration is 4 m/s^2 = 4*pixles_per_metre / (fsp)^2
createCanvas(roadlength, 100);
system = new Traffic();
light = new TrafficLight();
lights = [ light ];
}
function draw() {
background(220);
system.run();
if ( light.isOn() ) {
stroke(255,0,0);
fill(127, 0,0);
} else {
stroke(0,255,0);
fill(0, 127, 0);
}
strokeWeight(2);
ellipse(light.position, 36, 12, 12);
}
// A class for cars
var Car = function() {
// A car enters the simulation with constant velocity with a random value at some reasonable range.
this.acceleration = createVector(0, 0);
this.velocity = createVector(random(1, max_speed), 0);
this.position = createVector(0, roadpos_y);
this.length = car_size;
this.width = car_size;
this.max_acceleration = 4/sq(fps); // 4 m/s^2
this.max_deceleration = -8/sq(fps); // 8 m/s^2
};
Car.prototype.run = function() {
this.update();
this.display();
};
Car.prototype.breakDist = function() {
// Returns the breaking distance of the car.
return sq(this.velocity.x)/2/abs(this.max_deceleration);
}
Car.prototype.distToNext = function() {
dist = 1000;
for (i=0; i < cars.length; i++) {
tmp = cars[i].position.x - this.position.x;
if (tmp > 0 && tmp < dist) { dist = tmp; }
}
return dist;
}
Car.prototype.distToLight = function() {
dist = 1000;
if (light.isOn()) {
for (i=0; i < lights.length; i++) {
tmp = lights[i].position - this.position.x;
if (tmp > 0 && tmp < dist) { dist = tmp; }
}
}
return dist;
}
// Method to update position
Car.prototype.update = function() {
sep = min(this.distToNext(), this.distToLight());
text(int(sep), this.position.x, this.position.y-10);
// See if we have to slow down or speed up.
// Slow down if we're within breaking distance of the minimum separation.
text(int(this.breakDist()*10)/10, this.position.x, this.position.y-20);
//if (sep < min_separation) { this.velocity.x = 0; }
// Set the acceleration from the policy:
// If at or below breaking distance, break as hard as possible.
// Three times breaking distance:
if (sep < this.breakDist() + min_separation) {
this.acceleration.x = -8 + this.max_deceleration;
// } else if ( sep < 1*this.breakDist ) {
// this.acceleration.x = -sq(this.velocity.x)/2/(sep + min_separation);
} else {
this.acceleration.x = this.max_acceleration;
}
this.acceleration.x = constrain(this.acceleration.x, this.max_deceleration, this.max_acceleration);
this.velocity.add(this.acceleration.x);
this.velocity.x = constrain(this.velocity.x, 0, max_speed);
//if (this.velocity.x > max_speed) {
// this.velocity.x = max_speed;
//}
//if (this.velocity.x < 0) {
// this.velocity.x = 0;
//}
this.position.add(this.velocity);
text(int(this.velocity.x*100)/100, this.position.x, this.position.y+20);
};
// Method to display
Car.prototype.display = function() {
stroke(200, 255);
strokeWeight(2);
fill(127, 255);
ellipse(this.position.x, this.position.y, this.length/2, this.width/2);
};
// Is the car still on the road?
Car.prototype.isGone = function(){
return this.position.x > roadlength;
};
var Traffic = function() {
this.cars = [];
};
Traffic.prototype.addCar = function() {
// Limit the number of cars to 100 and only adda new one if there is space at the start.
if (cars.length < 100 && this.isClear() && int(random(9)) == 0) {
cars.push(new Car());
}
};
Traffic.prototype.isClear = function() {
r = true;
for (i = 0; i < cars.length; i++) {
if (cars[i].position.x < 0.5*sq(max_speed)/2/abs(-8/sq(fps))) {
r = false;
}
}
//console.log(r);
return r;
}
Traffic.prototype.run = function() {
console.log(cars.length);
this.addCar();
for (var i = cars.length-1; i >= 0; i--) {
// noprotect
var c = cars[i];
c.run();
if (c.isGone()) {
cars.splice(i, 1);
console.log(cars.length);
}
}
};