Interactive_DeepRL_Demo / js /bodies /abstract_body.js
ClementRomac's picture
ClementRomac HF staff
Added interactive demo with some policies
09a6f7f
raw
history blame
5.05 kB
/**
* @classdesc Abstract class for agent's morphologies
*/
class AbstractBody {
/**
* @constructor
* @param scale {number} - Scale of the environment
* @param motors_torque {number}
*/
constructor(scale, motors_torque){
this.SCALE = scale;
this.MOTORS_TORQUE = motors_torque;
this.body_parts = [];
this.motors = [];
this.is_selected = false;
}
/**
* Gets the size of the motors state.
* @return {number}
*/
get_state_size(){
return this.get_motors_state().length;
}
/**
* Gets the motors state.
* @return {Array}
*/
get_motors_state(){
let state = [];
for(let motor of this.motors){
let motor_info = motor.GetUserData();
if(motor_info.check_contact){
let s = [
motor.GetJointAngle() + motor_info.angle_correction,
motor.GetJointSpeed() / motor_info.speed_control,
0.0
]
if(motor_info.contact_body.GetUserData().has_contact){
s[2] = 1.0;
}
state = state.concat(s);
}
else{
state = state.concat([
motor.GetJointAngle() + motor_info.angle_correction,
motor.GetJointSpeed() / motor_info.speed_control
])
}
}
return state;
}
/**
* Gets the size of the action space.
* @return {number}
*/
get_action_size(){
return this.motors.length;
}
/**
* Activates the motors according to the given actions by setting the motors speed and torque.
* @param actions {Array}
*/
activate_motors(actions){
for(let i = 0; i < this.motors.length; i++){
this.motors[i].SetMotorSpeed(this.motors[i].GetUserData().speed_control * Math.sign(actions[i]));
let clamp01 = Math.max(0, Math.min(Math.abs(actions[i]), 1));
this.motors[i].SetMaxMotorTorque(this.MOTORS_TORQUE * clamp01);
}
}
/**
* Creates the Box2D body parts, joints and sensors of the agent.
* @param world {Object} - Box2D world
* @param init_x {number}
* @param init_y {number}
* @param force_to_center {number}
*/
draw(world, init_x, init_y, force_to_center){}
/**
* Gets all the body parts
* @return {Array}
*/
get_elements_to_render(){
return this.body_parts;
}
/**
* Checks if the given position is inside the agent's morphology
* @param pos {{x: number, y: number}}
* @return {boolean}
*/
isPosInside(pos){
for(let body of this.body_parts){
let shape = body.GetFixtureList().GetShape();
let vertices = [];
for(let i = 0; i < shape.m_count; i++){
let world_pos = body.GetWorldPoint(shape.m_vertices[i]);
vertices.push({x: world_pos.x, y: world_pos.y});
}
// Counts the number of intersections between the edges of the polygon and the line of equation y = pos.y which are to the right of pos.x
let nb_intersections = 0;
for(let i = 0; i < vertices.length; i++){
let v1 = vertices[i];
let v2;
if(i == vertices.length - 1){
v2 = vertices[0];
}
else {
v2 = vertices[i+1];
}
// Checks if the edge between v1 and v2 cross the mouse y-coordinate
if(pos.y >= Math.min(v1.y, v2.y) && pos.y <= Math.max(v1.y, v2.y)){
let intersection_x;
// Computes the equation of the line between v1 and v2
let a = (v2.y - v1.y) / (v2.x - v1.x);
let b = v1.y - a * v1.x;
// Computes the x-coordinate of the intersection point
if(Math.abs(a) == Infinity){
intersection_x = v1.x;
}
else{
intersection_x = (pos.y - b) / a;
}
// Increases the number of intersection only if the intersection point is to the right of the mouse x-coordinate
if(intersection_x >= pos.x) {
nb_intersections += 1;
}
}
}
// The pos is inside the agent's body if there is an odd number of intersections, else it is outside
if(nb_intersections % 2 != 0){
return true;
}
}
return false;
}
/**
* Destroys all the body parts of the agents.
* @param world {Object} - Box2D world
*/
destroy(world){
for(let body of this.body_parts){
world.DestroyBody(body);
}
this.body_parts = [];
this.motors = [];
}
}