## October 18, 2011

### Interobject Communication

The behaviors of several objects can be coordinated by having them exchange information using private chat protocols.   One object emits chat using llRegionSay(). Another object has a listen() event which receives messages on the same channel.

The sender would have code like this

integer commChannel = -837465;
...
llRegionSay( commChannel, "start");
...

The receiver would have code like this

integer commChannel = -837465;
default {
state_entry() {
llListen( commChannel, "", NULL_KEY, "");
}
listen( integer channel, string fromName,
key fromKey, string msg ){
if ( channel == commChannel ) {
if ( msg == "start" ) {
llSay(PUBLIC_CHANNEL, "'"+fromName+"' says start");
} else if ( msg == "stop" ) {
llSay(PUBLIC_CHANNEL, "'"+fromName+"' says stop");
}
}
}
}
____________________
Communication often consists pairs of messages that comprise a transaction.  For a ping-pong transaction which allows one object to find many other objects, the receiver would have code like this

listen( integer channel, string fromName, key fromKey,
string msg ){
if ( channel == commChannel ) {
integer match = llListFindList(
[ "start", "stop", "ping" ], [msg] );
if ( match == 0 ) {
....
} else if ( match == 1 ) {
....
} else if ( match == 2 ) {
llRegionSayTo( fromKey, commChannel, "pong" );
}
}
}

The receiver knows that the pong should go back to the emitter of the ping.  Therefore the pong emitter may use llRegionSayTo() to send the message to a specific object.

The pinger code would include a listen handle that receives the pong message.  To make a list of responder keys it could have code like this:

list pongerKeyList = [];

listen( integer channel, string fromName,
key fromKey, string msg ){
if ( channel == commChannel ) {
integer match = llListFindList( [ "pong" ], [msg] );
if ( match == 0 ) {
pongerKeyList += fromKey;
}
}
}
____________________
The receiver of a message can extract substantial information from the key of the sender using llGetObjectDetails().

listen( integer channel, string fromName,
key fromKey, string msg ){
if ( channel == commChannel ) {
if ( msg == "pong" ) {
list a = llGetObjectDetails( fromKey,
[ OBJECT_NAME, OBJECT_OWNER,
OBJECT_POS, OBJECT_ROT ] );
llSay(PUBLIC_CHANNEL, "pong response: "+llList2CSV(a));
}
}
}
____________________
Messages carry additional information by concatenating values using a special character pattern which does not appear within any values.   The message sender uses llDumpList2String() to build the message.  The receiver used llParseStringKeepNulls() to separate the message into a list of string fields.  These string fields are converted back into values using casts.    The ping sender would have code like this:

touch_start( integer nTouch ) {
float jitterPongMax = 4;
llRegionSay( commChannel, llDumpList2String(
[ "ping",
jitterPongMax , llGetUnixTime(),
llGetPos() ], "|"));
}

The ping receiver/pong sender would have code like this:

listen( integer channel, string fromName,

key fromKey,   string msg ){
if ( channel == commChannel ) {
list a = llParseStringKeepNulls( msg, ["|"], [] );
string verb = llList2String(a,0);
integer match = llListFindList(
[ "start", "stop", "ping" ], [verb] );
if ( match == 0 ) {
...
} else if ( match == 1 ) {       ...
} else if ( match == 2 ) {
float jitterMax = (integer)llList2String(a,0);
integer sendTime = (integer)llList2String(a,1);
vector pingerPos = (vector) llList2String(a,3);
llSleep( llFrand(jitterMax ) );

llRegionSayTo( fromKey, commChannel, "pong" );
....      }
}
}
____________________
Notes:

• If your system will be deployed across region boundaries, then use llShout(), which has a range of 100 meters.
• If the messages pass between one agent's attachments, use llWhisper(), which has a range of 10 meters.
• In the last example llSleep(llFrand(jitterMax)) prevents all the pongers from responding at the same time.  There are limits to how many messages can be queued by a region.
• llParseStringKeepNulls() is used because string values may be zero length strings.
• llListFindList() and subsequent integer comparisons is more efficient that a series of string comparisons.
Revised 6Jun2012
[end]

## February 12, 2011

### Uniform Motion Using llMoveToTarget

When an object is physical, the script function

$\bg_black \fn_phv llMoveToTarget( vector \vec{d}), float \tau );$

causes an object to drift smoothly toward a target position.  The drawback is that the motion is not uniform.  The velocity decreases toward zero as the target is approached.

This movement is approximated by

$\bg_black \fn_phv \vec{p}(t) - \vec{p}(0) = ( \vec{d} - \vec {p}(0) ) ( 1 - \exp {(- t / \tau) })$

The object is at position $\inline \bg_black \fn_phv \vec {p}(t)$ at time t.  Because of the exponential attenuation of the velocity, it will reach the position $\inline \bg_black \fn_phv \vec {d}$ after several intervals of the time interval $\inline \bg_black \tau$.

The object motion is made more uniform by using only an early portion of this exponential motion.

To create this effect, calculate a target position $\inline \bg_black \fn_phv \vec {D}$ which lays beyond $\inline \bg_black \fn_phv \vec {d}$, then terminate the movement at the time $\inline \bg_black \tau$.   In algebraic terms, solve this equation for $\inline \bg_black \fn_phv \vec {D}$:

$\inline \bg_black \vec{d} - \vec{p}(0) = (\vec{D} - \vec{p}(0))(1 - \exp{(-\tau / \tau )})$

The solution is

$\inline \bg_black \vec{D} = \vec{p}(0) + ( \vec{d} - \vec{p}(0))( e / ( e - 1))$

where e is the base of the natural logarithms, 2.7182818284....

For a target position d_target, a single step movement is shown below as code fragments.

float tau = 4;
vector d_target = <110,200,30>;
float e = 2.7182818284;

touch_start( integer nTouch ) {
float f = e / ( e - 1 );
vector D_target = llGetPos() + f *( d_target - llGetPos() );
llMoveToTarget( D_target, tau );
llSetTimerEvent( tau );
}

timer() {
llStopMoveToTarget();
llSetTimerEvent( 0 );
}

A more nuanced implementation would declare a list of d_target positions.  The timer event would move thought the list, calculating a series of D_targets, then using llMoveToTarget().  At the end of the list, the timer event would either return to the beginning of the list, or use llStopMoveToTarget().

Des.de.mona

[end]

## February 7, 2011

### Embedding Equations

Given that blogspot does not intrinsically support equations, I am experimenting with the LaTeX converter found at codecogs.

$\bg_black \fn_phv llMoveToTarget( \vec{p}, \tau )$

$\bg_black \fn_phv \vec{F}_g=-F\frac{m_1 m_2}{r^2} \vec{e}_r$

$\bg_black \fn_phv \frac{d}{dx}\ln(x)=\frac{1}{x}$

While this relies on the continued existence of the codecogs website, the countervailing advantage of this method is that the LaTeX input statement used to generate an equation becomes embedded in the web page.   For example, if you look at the source of this web page, you will see that the above equation for the derivative of the natural logarithm is generated using this statement:

\frac{d}{dx}\ln(x)=\frac{1}{x}

Should codecogs cease to exist, the LateX input statement has been preserved and may be used with any converter to regenerate the typeset image.  As for LaTeX itself, it is one of those Unix things that has been around for 30 years and is now distributed under the LaTeX Project Public License.

Des.de.mona