File size: 5,049 Bytes
09a6f7f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * @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 = [];
    }
}