# Curves Toy

Please take a moment to consider if this thread is worth bumping.

## Recommended Posts

So, this is one of the worst ways to go about this (1 object+script per point adds up fast) But it's quite fun anyway.

```// general vars:
integer gChan = 297;
integer DEBUG = FALSE;

vector gPos; // where is the point?

string gName;
string gDesc;

// interpolated variables:
vector gPointA; // location of point 'A'
string gCodePA; // actual letter may not be 'A'
vector gTangentA1; // location of tangent point
vector gPointB; // location of point 'B'
string gCodePB; // actual letter may not be 'B'
vector gTangentB0; // location of tangent point.
float gPercent; // percent of the way between 'A' and 'B'
// tangent variables:

// gCodePA ; will be overused as the point the tangent line passes through.
// gPointA ; similarly the position that point is at.
integer gTangentNum; // either 0 or 1, which tangent point is this.
integer gIgnore; // we need to ignore some messages to prevent an infinite back and forth.

// curve_end variables:
// none specific.

set_interpolation()
{
float x = gPercent;
float nx = 1-gPercent;
llSetRegionPos(nx*nx*nx*gPointA + 3*x*nx*nx*gTangentA1 + 3*x*x*nx*gTangentB0 + x*x*x*gPointB);
}

particle_line(key Target)
{
llParticleSystem([
PSYS_PART_FLAGS,
PSYS_SRC_TARGET_KEY, Target,
PSYS_PART_START_SCALE, <0.1,0.1,0.1>,
PSYS_PART_START_COLOR, <0.9,0.9,0.0>,
PSYS_SRC_BURST_RATE, 0.2,
PSYS_PART_MAX_AGE, 3.0
]);
}

default
{
state_entry()
{
if(DEBUG) llSay(0,"Boot up");
// set_state();
gName = llGetObjectName();
if      (!llSubStringIndex(gName,"Point"))
{   state curve_end;
}else if(!llSubStringIndex(gName,"Tangent"))
{   state tangent;
}else if(!llSubStringIndex(gName,"Interpolated"))
{   state interpolated;
}else
{   llSay(0,"Name not recognised");
}
}
}
state curve_end
{   state_entry()
{   //llListen(gChan,"","","");
llParticleSystem([]);
llSetColor(<0,1,0>,0);
llSetScale(<0.2,0.2,0.2>);
llSetText(gName,<0,1,0>,1.0);
}
touch_end(integer n)
{   // set_state();
if(gName!=llGetObjectName())
{   llResetScript();
}
vector pos = llGetPos();
if(gPos!=pos)
{   llSay(gChan,(string)pos);
gPos=pos;
}
}
}
state tangent
{   state_entry()
{   llParticleSystem([]);
llListen(gChan,"","","");
llSetColor(<0.9,0.9,0>,0);
llSetScale(<0.15,0.15,0.15>);
llSetText(gName,<1,1,0>,1.0);
gCodePA = llGetSubString(gName,-2,-2);
gTangentNum = (integer)llGetSubString(gName,-1,-1);
llSensor("Point "+gCodePA, "", PASSIVE|SCRIPTED, 12.0, PI);
}
on_rez(integer i)
{   llSensor("Point "+gCodePA, "", PASSIVE|SCRIPTED, 12.0, PI);
}
sensor(integer n)
{   if(n==1)
{   gPointA = llDetectedPos(0);
particle_line(llDetectedKey(0));
if(DEBUG) llSay(0,"Found point: "+gCodePA);
}else
{   llSay(0,"Error, duplicate points found: "+gCodePA);
}
}
no_sensor()
}
touch_start(integer n)
{   if(gName!=llGetObjectName())
{   llResetScript();
}
vector pos = llGetPos();
if(gPos!=pos)
{   llSay(gChan,(string)(gPos=pos));
gIgnore = TRUE;
vector X = llVecNorm(gPointA-llGetPos());
llSetRot(llRotBetween(<0,0,1>,X));
}
}
listen(integer Channel, string Name, key ID, string Text)
{
if(Name=="Point "+gCodePA)
{   vector pos = ((vector)Text);
llSetRegionPos(gPos = (llGetPos()+ pos - gPointA));
llSay(gChan,(string)gPos);
gIgnore = TRUE;
gPointA = pos;
vector X = llVecNorm(gPointA-llGetPos());
llSetRot(llRotBetween(<0,0,1>,X));
}else if(Name=="Tangent "+gCodePA+(string)(!gTangentNum))
{ // ^^ could be more efficient by precalculating that as a global variable.
if(gIgnore)
{   gIgnore=FALSE;
}else
{
vector pos2 = (vector)Text;
vector norm = llVecNorm(gPointA-pos2);
float dist = llVecDist(llGetPos(),gPointA);
vector gPos = gPointA+(norm*dist);
llSetRegionPos(gPos);
llSay(gChan,(string)gPos);
}
}
}
}
state interpolated
{   state_entry()
{   llParticleSystem([]);
llListen(gChan,"","","");
llSetColor(<0.2,0.2,0.2>,0);
llSetScale(<0.1,0.1,0.1>);

gDesc = llGetObjectDesc();

gCodePA = llGetSubString(gName,-2,-2);
gCodePB = llGetSubString(gName,-1,-1);
llSetText(gCodePA+gCodePB,<0.1,0.1,0.1>,1.0);
list parsed = llParseString2List(llGetObjectDesc(),["/"," "],[]);
gPercent = (float)llList2String(parsed,0) / (float)llList2String(parsed,1);
llSensor("Point "+gCodePA, "", PASSIVE|SCRIPTED, 12.0, PI);

}
on_rez(integer i)
{   llSensor("Point "+gCodePA, "", PASSIVE|SCRIPTED, 12.0, PI);
}
touch_start(integer n)
{   if(gName!=llGetObjectName() || gDesc!=gDesc)
{   llResetScript();
}
}
sensor(integer n)
{   while(~--n)
{   string name = llDetectedName(n);
if(name == "Point "+gCodePA)
{   gPointA = llDetectedPos(n);
llSensor("Point "+gCodePB, "", PASSIVE|SCRIPTED, 12.0, PI);
}else if(name == "Point "+gCodePB)
{   gPointB = llDetectedPos(n);
llSensor("Tangent "+gCodePA+"1", "", PASSIVE|SCRIPTED, 12.0, PI);
}else if(name == "Tangent "+gCodePA+"1")
{   gTangentA1 = llDetectedPos(n);
llSensor("Tangent "+gCodePB+"0", "", PASSIVE|SCRIPTED, 12.0, PI);
}else if(name == "Tangent "+gCodePB+"0")
{   gTangentB0 = llDetectedPos(n);
set_interpolation();
}
if(DEBUG) llSay(0,"Found: "+name);
}
}
no_sensor()
}
listen(integer Channel, string name, key ID, string Text)
{
vector pos = (vector)Text;
integer change = FALSE;
if(name == "Point "+gCodePA)
{   gPointA = pos;
change = TRUE;
}else if(name == "Point "+gCodePB)
{   gPointB = pos;
change = TRUE;
}else if(name == "Tangent "+gCodePA+"1")
{   gTangentA1 = pos;
change = TRUE;
}else if(name == "Tangent "+gCodePB+"0")
{   gTangentB0 = pos;
change = TRUE;
}
if(change)
{   set_interpolation();
}
}
}```

It's a bit of a pain to set up (need to type in a lot of repetitive names and descriptions) and to use (need to touch after moving the points or tangents to register a change) but it's workable. Edit-> move a point or 'tangent' then touch it and everything else falls into place in a 'natural' way.

You can see it live (I even left move permissions on, so anyone can move the colored points) in my sandbox http://maps.secondlife.com/secondlife/Gilgamesh/36/167/2012 until I decide I need the LI back. (should be a week or two at least)

• 2