1 /* An extremely simple physics package
2 Copyright (C) 2005-2008, Jonathan Bachrach, Jacob Beal, and contributors
3 listed in the AUTHORS file in the MIT Proto distribution's top directory.
5 This file is part of MIT Proto, and is distributed under the terms of
6 the GNU General Public License, with a linking exception, as described
7 in the file LICENSE in the MIT Proto distribution's top directory. */
10 #include "simpledynamics.h"
11 #include "visualizer.h"
13 /*****************************************************************************
15 *****************************************************************************/
17 #define elt_at(x, i) ((flo*)x)[i]
18 #define elt_ati(x, i) &((flo*)x)[i]
19 // This structure can be used to represent either a 3D point or 3D vector
21 static inline Vek
* vek_fil (Vek
*x
, flo val
) {
28 static inline Vek
* vek_mul (Vek
* x
, flo val
) {
31 *elt_ati(x
, i
) *= val
;
35 static void vek_out (Vek
* x
) {
38 post(" %.2f", elt_at(x
, i
));
41 static inline Vek
* vek_cpy (Vek
* x
, Vek
* y
) {
44 *elt_ati(x
, i
) = elt_at(y
, i
);
48 static inline Vek
* vek_set (Vek
* v
, flo x
, flo y
, flo z
) {
55 static inline Vek
* vek_add (Vek
* d
, Vek
* s
) {
58 *elt_ati(d
, i
) += elt_at(s
, i
);
62 static inline Vek
* vek_sub (Vek
* d
, Vek
* s
) {
65 *elt_ati(d
, i
) -= elt_at(s
, i
);
69 static inline flo
vek_dot (Vek
* x
, Vek
* y
) {
73 d
+= elt_at(x
, i
) * elt_at(y
, i
);
77 static inline flo
vek_len_sqr (Vek
* x
) {
81 static inline flo
vek_len (Vek
* x
) {
82 return (flo
)sqrt((double)vek_len_sqr(x
));
85 static inline flo
vek_dst_sqr (Vek
* x
, Vek
* y
) {
89 return vek_len_sqr(&t
);
92 static inline flo
vek_dst (Vek
* x
, Vek
* y
) {
93 return (flo
)sqrt(vek_dst_sqr(x
, y
));
96 /*****************************************************************************
98 *****************************************************************************/
99 static flo q
[4] = {1,0,0,0}; // null orientation quaternion
100 static flo w
[3] = {0,0,0}; // null angular velocity vector
101 const flo
* SimpleBody::orientation() {return q
; }
102 const flo
* SimpleBody::ang_velocity() { return w
; }
104 SimpleBody::~SimpleBody() { parent
->bodies
.remove(parloc
); }
106 void SimpleBody::preupdate() { set_velocity(0,0,0); }
108 // assumes display is centered
109 void SimpleBody::visualize() {
112 if(!parent
->is_show_bot
) return; // don't display unless should be shown
113 palette
->use_color(SIMPLE_BODY
);
115 glScalef(radius
, radius
, radius
);
116 if (parent
->is_mobile
) {
118 if (parent
->parent
->volume
->dimensions()==3) {
120 glRotatef(90.0, 1.0, 0.0, 0.0);
124 glRotatef(90.0, 0.0, 1.0, 0.0);
129 if (parent
->is_show_heading
) {
134 if(l
>0) vek_mul(&vec
,1/l
); // normalize vel
135 glVertex3f(vec
.x
, vec
.y
, vec
.z
);
147 void SimpleBody::render_selection() {
150 glScalef(radius
, radius
, radius
);
151 if (parent
->parent
->volume
->dimensions()==3) {
153 glRotatef(90.0, 1.0, 0.0, 0.0);
157 glRotatef(90.0, 0.0, 1.0, 0.0);
165 void SimpleBody::dump_state(FILE* out
, int verbosity
) {
167 if(parent
->dumpmask
& 0x01) fprintf(out
," %.2f",p
[0]);
168 if(parent
->dumpmask
& 0x02) fprintf(out
," %.2f",p
[1]);
169 if(parent
->dumpmask
& 0x04) fprintf(out
," %.2f",p
[2]);
170 if(parent
->dumpmask
& 0x08) fprintf(out
," %.2f",v
[0]);
171 if(parent
->dumpmask
& 0x10) fprintf(out
," %.2f",v
[1]);
172 if(parent
->dumpmask
& 0x20) fprintf(out
," %.2f",v
[2]);
173 if(parent
->dumpmask
& 0x40) fprintf(out
," %.2f",radius
);
175 fprintf(out
,"Position=[%.2f %.2f %.2f], ",p
[0],p
[1],p
[2]);
176 fprintf(out
,"Velocity=[%.2f %.2f %.2f] (Speed=%.2f), ",v
[0],v
[1],v
[2],
177 sqrt(v
[0]*v
[0] + v
[1]*v
[1] + v
[2]*v
[2]));
178 fprintf(out
,"Radius=%.2f\n",radius
);
182 /*****************************************************************************
184 *****************************************************************************/
185 Point
north_wall_normal ( 0.0, -1.0, 0.0 );
186 Point
south_wall_normal ( 0.0, 1.0, 0.0 );
187 Point
east_wall_normal ( -1.0, 0.0, 0.0 );
188 Point
west_wall_normal ( 1.0, 0.0, 0.0 );
189 Point
top_wall_normal ( 0.0, 0.0, -1.0 );
190 Point
bottom_wall_normal( 0.0, 0.0, 1.0 );
192 Point
* wall_normals
[N_WALLS
]
193 = { &north_wall_normal
, &south_wall_normal
, &east_wall_normal
,
194 &west_wall_normal
, &top_wall_normal
, &bottom_wall_normal
};
196 Point
north_wall_point ( 0.0, 1.0, 0.0 );
197 Point
south_wall_point ( 0.0, -1.0, 0.0 );
198 Point
east_wall_point ( 1.0, 0.0, 0.0 );
199 Point
west_wall_point ( -1.0, 0.0, 0.0 );
200 Point
top_wall_point ( 0.0, 0.0, 1.0 );
201 Point
bottom_wall_point ( 0.0, 0.0, -1.0 );
203 Point
* wall_points
[N_WALLS
]
204 = { &north_wall_point
, &south_wall_point
, &east_wall_point
,
205 &west_wall_point
, &top_wall_point
, &bottom_wall_point
};
207 /*****************************************************************************
209 *****************************************************************************/
210 #define K_BODY_RAD 0.0870 // constant matched against previous visualization
211 #define MAX_V 100 // default ceiling on velocity
212 SimpleDynamics::SimpleDynamics(Args
* args
, SpatialComputer
* parent
, int n
)
213 : BodyDynamics(parent
) {
214 flo width
=parent
->volume
->r
- parent
->volume
->l
;
215 flo height
=parent
->volume
->t
- parent
->volume
->b
;
217 body_radius
= (args
->extract_switch("-rad")) ? args
->pop_number()
218 : K_BODY_RAD
*sqrt((width
*height
)/(flo
)n
);
219 is_show_heading
= args
->extract_switch("-h"); // heading direction tick
220 speed_lim
= (args
->extract_switch("-S"))?args
->pop_number():MAX_V
;
221 is_walls
= ((args
->extract_switch("-w") & !args
->extract_switch("-nw")))
223 is_hard_floor
= args
->extract_switch("-floor"); // equiv to "stalk"
224 is_mobile
= args
->extract_switch("-m");
225 is_show_bot
= !args
->extract_switch("-hide-body");
226 args
->undefault(&can_dump
,"-Ddynamics","-NDdynamics");
227 dumpmask
= args
->extract_switch("-Ddynamics-mask") ?
228 (int)args
->pop_number() : -1;
230 for (int j
=0; j
<N_WALLS
; j
++) {
231 walls
[j
].x
= wall_points
[j
]->x
*width
*0.4;
232 walls
[j
].y
= wall_points
[j
]->y
*height
*0.4;
233 if(parent
->volume
->dimensions()==3) {
234 Rect3
* r
= (Rect3
*)parent
->volume
;
235 walls
[j
].z
= wall_points
[j
]->z
*(r
->c
-r
->f
)*0.4;
237 walls
[j
].z
= wall_points
[j
]->z
;
240 // register to simulate hardware
241 parent
->hardware
.patch(this,MOV_FN
);
242 parent
->hardware
.patch(this,RADIUS_SET_FN
);
243 parent
->hardware
.patch(this,RADIUS_GET_FN
);
247 BOOL
SimpleDynamics::handle_key(KeyEvent
* key
) {
248 if(key
->normal
&& !key
->ctrl
) {
250 case 'w': is_walls
= !is_walls
; return TRUE
;
251 case 'b': is_show_bot
= !is_show_bot
; return TRUE
;
252 case 'm': is_mobile
= !is_mobile
; return TRUE
;
253 case 'h': is_show_heading
= !is_show_heading
; return TRUE
;
259 void SimpleDynamics::visualize() {
260 // only the walls need any drawing here: all else goes through Device
261 // and we aren't drawing walls, either
264 Body
* SimpleDynamics::new_body(Device
* d
, flo x
, flo y
, flo z
) {
265 SimpleBody
* b
= new SimpleBody(this,d
,x
,y
,z
,body_radius
);
266 b
->parloc
= bodies
.add(b
);
270 void SimpleDynamics::dump_header(FILE* out
) {
272 if(dumpmask
& 0x01) fprintf(out
," \"X\"");
273 if(dumpmask
& 0x02) fprintf(out
," \"Y\"");
274 if(dumpmask
& 0x04) fprintf(out
," \"Z\"");
275 if(dumpmask
& 0x08) fprintf(out
," \"V_X\"");
276 if(dumpmask
& 0x10) fprintf(out
," \"V_Y\"");
277 if(dumpmask
& 0x20) fprintf(out
," \"V_Z\"");
278 if(dumpmask
& 0x40) fprintf(out
," \"RADIUS\"");
283 /*****************************************************************************
284 * INCREMENTAL EVOLUTION & ACTUATION *
285 *****************************************************************************/
286 // Note: although the old sim-bot physics spoke of force, they act only at
287 // the level of velocity.
289 // in simple dynamics, just step based on velocity and position
290 #define K_BOUND 0.75 // restoring force from walls
291 BOOL
SimpleDynamics::evolve(SECONDS dt
) {
292 if(!is_mobile
) return FALSE
;
293 for(int i
=0;i
<bodies
.max_id();i
++) {
294 Body
* b
= (Body
*)bodies
.get(i
);
296 // device moves itself
297 Vek
dp(b
->velocity());
298 flo len
= vek_dot(&dp
,&dp
);
299 if (len
> speed_lim
) vek_mul(&dp
,speed_lim
/len
); // limit velocity
301 // device is moved by walls
303 for (int j
=0; j
<N_WALLS
; j
++) {
304 Vek
vec(b
->position());
305 vek_sub(&vec
, &walls
[j
]);
306 flo d
= vek_dot(&vec
, wall_normals
[j
]);
308 vek_cpy(&vec
, wall_normals
[j
]);
309 vek_mul(&vec
, -d
* K_BOUND
* dt
);
314 // adjust the position
315 Vek
pos(b
->position());
316 b
->moved
= (dp
.x
||dp
.y
||dp
.z
);
318 // Hard floor at z=0: if the calculated pos has z < 0, reset to 0
319 if(is_hard_floor
&& pos
.z
<0) { pos
.z
=0; b
->moved
=TRUE
; }
320 b
->set_position(pos
.x
,pos
.y
,pos
.z
);
327 // There is no subtlety here: mov just sets velocity directly
328 void SimpleDynamics::mov(VEC_VAL
*v
) {
329 flo x
= NUM_GET(&v
->elts
[0]);
330 flo y
= NUM_GET(&v
->elts
[1]);
331 flo z
= v
->n
> 2 ? NUM_GET(&v
->elts
[2]) : 0.0;
332 device
->body
->set_velocity(x
,y
,z
);
334 // sensing & actuation of body radius
335 NUM_VAL
SimpleDynamics::radius_set (NUM_VAL val
)
336 { return ((SimpleBody
*)device
->body
)->radius
= val
; }
337 NUM_VAL
SimpleDynamics::radius_get (VOID
)
338 { return ((SimpleBody
*)device
->body
)->radius
; }