Spiral Staircase Generator

From Second Life Wiki
Jump to navigation Jump to search
A screenshot of a staircase generated by the Spiral Staircase Generator.

Rezzes a spiral staircase according to configuration parameters.

Generator

Drop this in the generator prim, along with the step, banister, and baluster.

////////////////////////////////
// Spiral Staircase Generator //
// By Meyermagic Salome       //
////////////////////////////////
// Staircase Configuration //
float wh = 2.5;	//Height of one wave (in meters)
float wc = 4.0;	//Full waves in staircase

float r_max = 10.0;	//Radius maximum, (0.1 -> 10.0)
float r_min = 7.5;	//Radius minimum, (0.1 -> 10.0)

integer landing = 5;	//Number of non-rising stairs at top of staircase (not included in wc)

//Step Configuration
float s_h = 0.01;	//Step height, (0.01 -> 0.99)
float s_s = 0.2;	//Vertical stair spacing, (0.01 -> 10.00)

float l_max = 1.0;	//Maximum stair length, (0.01 -> 10.00)
float l_min = 0.5;	//Minimum stair length, (0.01 -> 10.00)

//Balustrade Configuration
float bal_d = 0.05;	//Baluster diameter, (0.001 -> 1.000)
float bal_h = 0.5;	//Height of baluster / height of banister, (0.01 -> 1.00)

float ban_d = 0.05;	//Banister diameter, (0.001 -> 1.000)
float ban_h = 2.0;	//Banister height, (0.01 -> 10.00)

// Functions //
string str_repeat(string src, integer count)
{
    if(count > 0)
    {
        string output;
        integer i;
        for(i = 0; i < count; i++)
        {
            output += src;
        }
        return output;
    }
    else
    {
        return "";
    }
}

string pad_left(string str, string pad, integer len)//Left-pads string str to length len with character pas
{
    return str_repeat(pad, len - llStringLength(str)) + str;
}
list segment(vector a, vector b)//Returns the parameters for a cylinder connecting points a and b
{//Based on http://lslwiki.net/lslwiki/wakka.php?wakka=LibraryBezierCurveDemo
    vector mid = (a + b) / 2.0;
    vector localZ = b - a;

    float len = llVecMag(localZ);

    localZ = localZ / len;

    vector xAxis;

    if(localZ.x < localZ.y)
    {
        xAxis = <1.0, 0.0, 0.0>;
    }
    else
    {
        xAxis= <0.0, 1.0, 0.0>;
    }

    vector localX = xAxis - (localZ * xAxis) * localZ;
    localX = llVecNorm(localX);

    rotation rot = llAxes2Rot(localX, localZ % localX, localZ);

    return [len, mid, rot];
}

//Staircase-specific Functions
float wave(integer i)//Return i as portion of full wave (2PI)
{
    return PI * (float)i * s_s / wh;
}
float radius(integer i)//Return the stair radius at step i
{
    return llFabs(((llCos(wave(i)) + 1.0) * ((r_max - r_min) / 2)) + r_min);
}
float len(integer i)//Returns the length of stair at step i
{
    return llFabs(((llCos(wave(i)) + 1.0) * ((l_max - l_min) / 2)) + l_min);
}
float rot(integer i)//Returns the rotation of stair at step i
{
    return llAcos(1 - llPow(len(i), 2.0) / (2 * llPow(radius(i), 2)));
}

integer s_encode(integer i)
{
    return (integer)("1" + pad_left((string)llRound(s_h * 100.0), "0", 2) + pad_left((string)llRound(radius(i) * 10.0), "0", 3) + pad_left((string)llRound(len(i) * 100.0), "0", 4));
}
integer p_encode(float h)
{
    return (integer)("1" + pad_left((string)llRound(bal_d * 1000.0), "0", 4) + pad_left((string)llRound(h * 1000.0), "0", 5));
}
integer r_encode(float l)
{
    return (integer)("1" + pad_left((string)llRound(ban_d * 1000.0), "0", 4) + pad_left((string)llRound(l * 1000.0), "0", 5));
}

default
{
    touch_start(integer num_detected)
    {
        if(llDetectedKey(0) == llGetOwner())
        {
            integer i = 0;
            float t = 0.0;

            //Rez main rising steps
            for(i = 0; i < (integer)(wc * wh / s_s); ++i)
            {
                llSetPos(llGetPos() + <0, 0, s_s>);//I'll have it do this every ~10m when I get around to it

                float c_rot = rot(i);
                float c_radius = radius(i);
                float n_radius = radius(i + 1);
                vector m_pos = llGetPos();

                //Rez step
                llRezObject("Step",
                    m_pos + <llCos(t + c_rot / 2.0) * c_radius / 2.0, llSin(t + c_rot / 2.0) * c_radius / 2.0, 0>,
                    ZERO_VECTOR,
                    llEuler2Rot(<PI_BY_TWO, t + c_rot / 2.0 + 4.7123889, 0>),
                    s_encode(i));

                //Get balustrade data
                list b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                    m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0) + s_s>);
                if(i == (integer)(wc * wh / s_s) - 1)
                {
                    b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                        m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0)>);
                }
                vector b_pos = llList2Vector(b_data, 1);

                //Rez banister
                llRezObject("Banister",
                    b_pos,
                    ZERO_VECTOR,
                    llList2Rot(b_data, 2),
                    r_encode(llList2Float(b_data, 0)));

                //Rez baluster
                llRezObject("Baluster",
                    <b_pos.x, b_pos.y, m_pos.z + ((b_pos.z - m_pos.z) / 2.0) + (s_h / 2.0)>,
                    ZERO_VECTOR,
                    ZERO_ROTATION,
                    p_encode((b_pos.z - m_pos.z) * bal_h));

                //Increment rotation
                t += rot(i);
            }

            //Rez non-rising steps
            for(i = (integer)(wc * wh / s_s); i < (integer)(wc * wh / s_s) + landing; ++i)
            {
                float c_rot = rot(i);
                float c_radius = radius(i);
                float n_radius = radius(i + 1);
                vector m_pos = llGetPos();

                //Rez step
                llRezObject("Step",
                    m_pos + <llCos(t + c_rot / 2.0) * c_radius / 2.0, llSin(t + c_rot / 2.0) * c_radius / 2.0, 0>,
                    ZERO_VECTOR,
                    llEuler2Rot(<PI_BY_TWO, t + c_rot / 2.0 + 4.7123889, 0>),
                    s_encode(i));

                //Get balustrade data
                list b_data = segment(m_pos + <llCos(t) * c_radius, llSin(t) * c_radius, ban_h + (s_h / 2.0)>,
                    m_pos + <llCos(t + c_rot) * n_radius, llSin(t + c_rot) * n_radius, ban_h + (s_h / 2.0)>);
                vector b_pos = llList2Vector(b_data, 1);

                //Rez banister
                llRezObject("Banister",
                    b_pos,
                    ZERO_VECTOR,
                    llList2Rot(b_data, 2),
                    r_encode(llList2Float(b_data, 0)));

                //Rez baluster
                llRezObject("Baluster",
                    <b_pos.x, b_pos.y, m_pos.z + ((b_pos.z - m_pos.z) / 2.0) + (s_h / 2.0)>,
                    ZERO_VECTOR,
                    ZERO_ROTATION,
                    p_encode((b_pos.z - m_pos.z) * bal_h));

                //Increment rotation
                t += rot(i);
            }
        }
    }
}

Conf_Step

Drop this in a prim named "Step". (Box with x-taper 1.0 is recommended).

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float y = ((float)llGetSubString(d, 1, 2)) / 100.0;//Height
            float z = ((float)llGetSubString(d, 3, 5)) / 10.0;//Radius
            float x = ((float)llGetSubString(d, 6, 9)) / 100.0;//Length
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}

Conf_Banister

Drop this in a cylinder named "Banister".

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float z = ((float)llGetSubString(d, 5, 9)) / 1000.0;//Length
            float y = ((float)llGetSubString(d, 1, 4)) / 1000.0;//Diameter
            float x = y;
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}

Conf_Baluster

Drop this in a cylinder named "Baluster".

default
{
    on_rez(integer s)
    {
        if(s != 0)
        {
            string d = (string)s;
            float z = ((float)llGetSubString(d, 5, 9)) / 1000.0;//Height
            float y = ((float)llGetSubString(d, 1, 4)) / 1000.0;//Diameter
            float x = y;
            llSetScale(<x, y, z>);
            llRemoveInventory(llGetScriptName());
        }
    }
}