#VRML V2.0 utf8 PROTO AvoidBoxChaser [ field SFVec3f position 0 0 0 field SFVec3f goal 0 0 0 field SFFloat speed 1 field MFVec3f obstacles [ ] field SFInt32 index -1 eventIn SFVec3f set_goal eventIn MFVec3f obstacles_changed eventOut MFFloat report_position ] { Group { children [ DEF MOVEME Transform { translation IS position children Shape { appearance Appearance { material Material {} } geometry Box { size .5 .5 .5 } } } DEF HEART TimeSensor { loop TRUE } # update direction and position when receive "heartbeats" DEF SCRIPT Script { eventIn SFTime beat eventIn SFVec3f set_goal IS set_goal eventIn MFVec3f obstacles_changed IS obstacles_changed eventOut SFVec3f position_changed eventOut MFFloat report_position IS report_position field SFInt32 index IS index field SFVec3f direction 0 0 1 field SFVec3f position IS position field SFVec3f goal IS goal field SFFloat speed IS speed field SFTime lastBeat 0 field MFVec3f obstacles IS obstacles field SFBool first TRUE field SFFloat repulse 4 field SFFloat attract 2 field SFFloat power 2.5 url "vrmlscript: function repulseForce(obstacle) { v = position.subtract(obstacle); ods = repulse * 1/Math.pow(v.length(), power); return v.normalize().multiply(ods); } function calcDirection() { // compute force of attraction towards goal gforce = goal.subtract(position).normalize().multiply(attract); // computer repulsive force away from obstacles rforce = new SFVec3f(0,0,0); for(i=0; i<obstacles.length; i++) { if(i!=index) { r = repulseForce(obstacles[i]); rforce = rforce.add(r); } } force = gforce.add(rforce); direction = force.normalize(); } function set_goal(val) { goal = val; calcDirection(); } function beat(val) { if(first) { // initialize lastBeat on first heartbeat lastBeat = val; first = FALSE; } else { timeElapsed = val - lastBeat; calcDirection(); position = position.add(direction.multiply(speed*timeElapsed)); position_changed = position; report_position = new MFFloat(index, position.x, position.y, position.z); lastBeat = val; } } function obstacles_changed(val) { obstacles = val; } " } ] } ROUTE HEART.time TO SCRIPT.beat ROUTE SCRIPT.position_changed TO MOVEME.translation }