// Testing child prim rotations.
// This is a script that could be used with a non-path-cut linked door or windows
// and so on, with the correct rotation offsets and calculations to function.
//
// When the script is run for the first time, prim should be in the closed position.
//
// Kimm Paulino, August 2010
 
// Specify the rotation to apply, as a Euler vector in degrees
vector gNewRotation_e = <0.0, 0.0, 150.0>;
 
// Specify the details of the Centre of Rotation to use.
// Can either work it out from the prim parameters or put a hardcoded one in here
integer gCorAxis = 1;	// 1=x, 2=y, 3=z, -1=-x, -2=-y, -3=-z, 0=gCor
vector gCor = <0.0,0.0,0.0>;	// Relative to root prim, if gCorAxis = 0
 
// This test script will automatically return the prim to its start
// parameters after this time if gAutoClose is set to TRUE.
float RESET_TIME = 5.0;
integer gAutoClose = FALSE;
 
vector gInitialPosition;
rotation gInitialRotation;
integer CLOSED=0;
integer OPEN=1;
integer gState;
 
store_child ()
{
//    llOwnerSay ("-----------------");
    gInitialPosition = llGetLocalPos();
//    llOwnerSay ("Initial Position: " + (string)gInitialPosition);
    gInitialRotation = llGetLocalRot();
//    llOwnerSay ("Initial Rotation: " + (string)r2v(gInitialRotation));
}
 
restore_child()
{
    // Note: Use the PRIM_ROTATION workaround, as described in SVC-93
    llSetPrimitiveParams ([    PRIM_POSITION, gInitialPosition,
                                    PRIM_ROTATION, gInitialRotation / llGetRootRotation()]);
    //llSetPos (gInitialPosition);
    //llSetLocalRot (gInitialRotation);
}
 
vector calcCorAxis ()
{
    // Note: If the prim is rotated, then we need to apply the
    // same rotation to the size values to pick up the correct axis
    vector prim_size = llGetScale() * llGetLocalRot();
    if (gCorAxis == 1)
    {
        return <(prim_size.x/2.0), 0.0, 0.0>;
    }
    else if (gCorAxis == 2)
    {
        return <0.0, (prim_size.y/2.0), 0.0>;
    }
    else if (gCorAxis == 3)
    {
        return <0.0, 0.0, (prim_size.z/2.0)>;
    }
    else if (gCorAxis == -1)
    {
        return <(-prim_size.x/2.0), 0.0, 0.0>;
    }
    else if (gCorAxis == -2)
    {
        return <0.0, (-prim_size.y/2.0), 0.0>;
    }
    else if (gCorAxis == -3)
    {
        return <0.0, 0.0, (-prim_size.z/2.0)>;
    }
    else
    {
        return gCor;
    }
}
 
vector r2v (rotation r)
{
    return (RAD_TO_DEG * llRot2Euler (r));
}
 
// rot is a rotation to be applied to the prim
// cor is a relative position (to the root) for the centre of rotation
rotate_child (rotation rot, vector cor)
{
    // Work in local coordinates
    vector current_position = llGetLocalPos();
    rotation current_orientation = llGetLocalRot();
    //llOwnerSay ("Current position/rotation: " + (string)current_position + " / " + (string)r2v(current_orientation));
 
    // Calculate the offset from the centre of the object
    // to the centre of rotation.  This effectively moves
    // the object so that the cor can be thought off as the
    // origin.  Once we've done the calculations, we'll move it back.
    vector normalised_position = current_position - cor;
    //llOwnerSay ("Normalised position/COR: " + (string)normalised_position + " / " + (string)cor);
 
    // Calculate the new position by applying the required
    // rotation to the current position (i.e. rotate the
    // vector origin-position to produce origin-newposition)
    vector new_normalised_position = normalised_position * rot;
    //llOwnerSay ("Rotated Normalised Position: " + (string)new_normalised_position);
    vector new_position = cor + new_normalised_position;
    //llOwnerSay ("New Actual Position: " + (string)new_position);
 
    rotation new_orientation = current_orientation * rot;
    //llOwnerSay ("New Orientation: " + (string)r2v(new_orientation));
 
    // Set local position and rotation
    // Note: There is no llSetLocalPos - llSetPos will do local coords for a child
    //llSetPos (new_position);
    //llSetLocalRot (new_orientation);
 
    // However, use llSetPrimitiveParams to set both at same time, without
    // incurring two 0.2s delays ... although note, have to use
    // the PRIM_ROTATION workaround, as described in SVC-93
    llSetPrimitiveParams ([    PRIM_POSITION, new_position,
                                    PRIM_ROTATION, new_orientation / llGetRootRotation()]);
 
}
 
default
{
    on_rez (integer start_param)
    {
        // As positions will be stored on entry,
        // don't want to auto reset on rez, as it might
        // be rezed in the closed position, which would screw
        // things up!
//        llResetScript();
    }
 
    state_entry ()
    {
        // store initial position/etc
        gState = CLOSED;
        store_child ();
    }
 
    touch_start (integer num_detected)
    {
        if (gState == CLOSED)
        {
            // Need to convert CoR to local coordinates relative to
            // the root prim (not just relative to this prim).
            vector cor = llGetLocalPos() + calcCorAxis();
            rotate_child (llEuler2Rot (gNewRotation_e * DEG_TO_RAD), cor);
            if (gAutoClose)
            {
                llSetTimerEvent (RESET_TIME);
            }
            gState = OPEN;
        }
        else
        {
            restore_child ();
            gState = CLOSED;
        }
    }
 
    timer ()
    {
        gState = CLOSED;
        restore_child ();
        llSetTimerEvent (0.0);
    }
}