Jump to content

A Bi-Swing Door that opens Away from you


Rolig Loon
 Share

You are about to reply to a thread that has been inactive for 1856 days.

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

Recommended Posts

Builders sometimes ask for a door that will open away from you as you approach it -- think of a bi-swing door in a restaurant kitchen, for example.  There are several ways to write a script to detect where an av is, relative to a door or wall.  This script does it by locating the av relative to the door's "default" direction of swing, defined in its WhereAmI function. (The math is a little tricky, but it's worth studying as a problem in SL rotations and matrix algebra.)  As noted in embedded comments, you can use the script in a linked or a freestanding door.  If the door is in a linkset, just be sure that it is a child link and that the script is in the door itself.  As written, the door rotates on its Z axis and will determine for itself whether its X dimension is larger or smaller than the Y. Other possible geometries are left as a challenge for the reader.

Casual note: Scripters will recognize a genetic relationship to Void Singer's Simple Hinge Action script, which remains the most basic door script in SL. Some things are hard to beat.

// Open Other Way -- Rolig Loon -- April 2017

// This script always opens a door away from the person who touches it, then closes the door later automatically .

// The script may be used in a linked or unlinked door, but must be placed in the door itself. If the door is linked, it must be a child link.
// It was designed for a prim "cut" door (B:0.125, E:0.625 or B;0.375, E:0.875) but could be used for a mesh door hinged on one side.
// The script will detect whether the door's X dimension is larger or smaller than its Y dimension and adjust accordingly.
// When it is installed for the first time, be sure that the door is in its CLOSED rotation. NOTE: This is its LOCAL rotation, relative
//      to the root of the linkset, so it will not be affected if you move the entire linkset later.

// User parameters ===========

integer gIntSwing = 90; // Angular swing of the door, in degrees
float gfOpenTime = 5.0; // Amount of time before the door closes automatically, in seconds

// ===== Do not mess with anything below here unless you know what you are doing ============

rotation gHomeRot;
float gfHomeAngle;
integer giXisBigger;
rotation gRotSwing;
integer gDir;

integer IsClosed(rotation MyRot)    // Is the door closed?
{
    float MyAngle = llRot2Angle(MyRot);
    if (llFabs(MyAngle - gfHomeAngle) < 0.0523599) // Within 3 degrees of "closed"
    {
        llSetLocalRot(gHomeRot);    // Be sure it's closed now, just in case
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

integer WhereAmI (vector AvPos) // Where is the av, relative to the door's default swing direction?
{
    float dotP = llRot2Fwd(gHomeRot)*llVecNorm((AvPos - llGetPos())/llGetRot()- llGetLocalPos());
    if (giXisBigger)    // The door's X dimension is bigger than its Y dimension
    {
        return (RAD_TO_DEG*llAcos(dotP) < 90.0) ;   // TRUE or FALSE?
    }
    else    // The door's Y dimension is bigger than its X dimension 
    {
        return (RAD_TO_DEG*llAcos(dotP) > 90.0) ;   // TRUE or FALSE?
    }        
}

default
{
    state_entry()
    {
        // First, define the "closed" rotation for the door
        gHomeRot = llGetLocalRot();
        gfHomeAngle = llRot2Angle(gHomeRot);
        vector Size = llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_SIZE]),0);
        giXisBigger = (Size.x > Size.y);
        // and now, here's the default rotation to apply in swinging the door ....
        gRotSwing = llEuler2Rot( <0.0, 0.0, (float)gIntSwing * DEG_TO_RAD> );
    }
    
    touch_end( integer mum )
    {
        if ( IsClosed(llGetLocalRot()) )  //If the door is closed ...
        {
            gDir = 0;        
            if ( WhereAmI(llDetectedPos(0)) ) // Where is the av, relative to the door's default swing direction?
            {
                gDir = 1;
                // Reverse the door's default opening direction
                gRotSwing.s *= -1;
            }
            llSetLocalRot( gRotSwing * llGetLocalRot() ); 
            gRotSwing.s *= -1;  // And reverse direction again, ready for the return swing
            llSetTimerEvent(gfOpenTime);         
        }
    }
    
    timer()
    {
        // Now reverse the direction of gRotSwing and apply it....
        llSetLocalRot( gRotSwing * llGetLocalRot() );
        if (!gDir)  // If the door just opened in the default direction, we don't need to reverse it yet again
        {
            gRotSwing.s *= -1;
        }
        llSetTimerEvent(0.0); 
    }
}

 

  • Like 5
  • Thanks 1
Link to comment
Share on other sites

  • 2 weeks later...
Just now, Chic Aeon said:

@Ruthven Willenov  I am not a script writer (only a tinkerer) but every door I have acts that way. If you THINK about it, the real life doors do too. The ONLY open in one direction *wink*. 

The point of this one is that it is supposed to be bi-swing, a door that swings in both directions, as mentioned in the description above. A bar/saloon door, or a restaurant kitchen door for examples

Link to comment
Share on other sites

I'm on vacation far from home, so I can't log in to SL from a motel's Internet connection to see exactly what you are doing.  My best guess, though, is that your door is not hinged where the script expects it to be.  With a cut prim door, there are two possible choices, as the notes indicate.  The door is either flattened on its X axis or its Y axis, and the hinge is always its Z axis.  The script can distinguish between those two possibilities.  It can't guess correctly if you have a door built some other way, as a mesh door might be.

Also, once you get your door set up, be sure that the door is closed and then reset the script so that it knows what "closed" means.

Link to comment
Share on other sites

I figured out another solution by reversing the logic in this comment 

what I have now is:

integer isFacing(vector avpos)
{
    vector myPos = llGetPos();
    rotation myRot = llGetRot();
    vector facing;
    vector target = llVecNorm( avpos - myPos);
    float dp;
    if(giXisBigger)
    {
        facing = llRot2Left(myRot);
        dp = facing*target;
    }
    else
    {
        facing = llRot2Fwd( myRot);
        dp = -(facing*target);//had to reverse the dp when Y axis is bigger to make door swing the right direction
    }
    return ( dp > 0 );
}

and I use it in place of WhereAmI

Edited by Ruthven Willenov
cleaned up the math and if logic a bit
Link to comment
Share on other sites

  • 2 months later...

I wanted to play with slowing down the rotation so it looked more realistic than just snapping into open/close position. So I added in a steps variable, divided the rotation by that number of steps, and added a for loop to handle the stepped rotation. I may play with a target omega version

// Open Other Way -- Rolig Loon -- April 2017

// This script always opens a door away from the person who touches it, then closes the door later automatically .

// The script may be used in a linked or unlinked door, but must be placed in the door itself. If the door is linked, it must be a child link.
// It was designed for a prim "cut" door (B:0.125, E:0.625 or B;0.375, E:0.875) but could be used for a mesh door hinged on one side.
// The script will detect whether the door's X dimension is larger or smaller than its Y dimension and adjust accordingly.
// When it is installed for the first time, be sure that the door is in its CLOSED rotation. NOTE: This is its LOCAL rotation, relative
//      to the root of the linkset, so it will not be affected if you move the entire linkset later.

// User parameters ===========

integer gIntSwing = 90; // Angular swing of the door, in degrees
float gfOpenTime = 5.0; // Amount of time before the door closes automatically, in seconds

// ===== Do not mess with anything below here unless you know what you are doing ============

integer steps = 6;//How many steps it will take to open/close the door to give a more realistic swinging action
integer isFacing(vector avpos)
{
    vector myPos = llGetPos();
    rotation myRot = llGetRot();
    vector facing;
    vector target = llVecNorm( avpos - myPos);
    float dp;
    if(giXisBigger)
    {
        facing = llRot2Left(myRot);
        dp = facing*target;
    }
    else
    {
        facing = llRot2Fwd( myRot);
        dp = -(facing*target);//had to reverse the dp when Y axis is bigger to make door swing the right direction
    }
    return ( dp > 0 );
}

rotation gHomeRot;
float gfHomeAngle;
integer giXisBigger;
rotation gRotSwing;
integer gDir;

integer IsClosed(rotation MyRot)    // Is the door closed?
{
    float MyAngle = llRot2Angle(MyRot);
    if (llFabs(MyAngle - gfHomeAngle) < 0.0523599) // Within 3 degrees of "closed"
    {
        llSetLocalRot(gHomeRot);    // Be sure it's closed now, just in case
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

integer WhereAmI (vector AvPos) // Where is the av, relative to the door's default swing direction?
{
    float dotP = llRot2Fwd(gHomeRot)*llVecNorm((AvPos - llGetPos())/llGetRot()- llGetLocalPos());
    llSay(0,(string)dotP);
    if (giXisBigger)    // The door's X dimension is bigger than its Y dimension
    {
        return (RAD_TO_DEG*llAcos(dotP) < 0.0) ;   // TRUE or FALSE?
    }
    else    // The door's Y dimension is bigger than its X dimension 
    {
        return (RAD_TO_DEG*llAcos(dotP) > 0.0) ;   // TRUE or FALSE?
    }        
}

default
{
    state_entry()
    {
        // First, define the "closed" rotation for the door
        gHomeRot = llGetLocalRot();
        gfHomeAngle = llRot2Angle(gHomeRot);
        vector Size = llList2Vector(llGetLinkPrimitiveParams(LINK_THIS,[PRIM_SIZE]),0);
        giXisBigger = (Size.x > Size.y);
        // and now, here's the default rotation to apply in swinging the door ....
        gRotSwing = llEuler2Rot( <0.0, 0.0, (float)gIntSwing * DEG_TO_RAD>/steps );
    }
    
    touch_start( integer mum )
    {
        if ( IsClosed(llGetLocalRot()) )  //If the door is closed ...
        {
            gDir = 0;        
            if ( isFacing(llDetectedPos(0)) ) // Where is the av, relative to the door's default swing direction?
            {
                llSay(0,"other way");
                gDir = 1;
                // Reverse the door's default opening direction
                gRotSwing.s *= -1;
            }
            integer i;
            for(i = 0; i< steps;i++)
            {
                llSetLocalRot( gRotSwing * llGetLocalRot() ); 
            }
            gRotSwing.s *= -1;  // And reverse direction again, ready for the return swing
            llSetTimerEvent(gfOpenTime);         
        }
        else
        {
            integer i;
            for(i = 0; i< steps;i++)
            {
                llSetLocalRot( gRotSwing * llGetLocalRot() ); 
            }
        if (!gDir)  // If the door just opened in the default direction, we don't need to reverse it yet again
        {
            gRotSwing.s *= -1;
        }
        llSetTimerEvent(0.0); 
        }
    }
    
    timer()
    {
        // Now reverse the direction of gRotSwing and apply it....
        integer i;
            for(i = 0; i< steps;i++)
            {
                llSetLocalRot( gRotSwing * llGetLocalRot() ); 
            }
        if (!gDir)  // If the door just opened in the default direction, we don't need to reverse it yet again
        {
            gRotSwing.s *= -1;
        }
        llSetTimerEvent(0.0); 
    }
}

 

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Another good use for this, even if you don't have it swing away from the avatar, it's a good way to determine if they are coming or going from a store through the door. So instead of welcoming them when they are walking out, the greeter could instead say something like, "I hope you enjoyed your stay, please come again!"

Link to comment
Share on other sites

  • 3 months later...

This may be nitpicking, but since the script doesn't seem to work as-is and reading it seems confusing, I tried making my own implementation and after I was done with it I came to the conclusion that your method seems a little over-complicated. Maybe I'm just too bad at math to get it at first glance. Basically all of WhereAmI() is confusing me.

This is what I came up with (works linked and unlinked, put the script directly into the door, door is assumed to be "cut in half" like Rolig's):

integer open;
integer swing_deg = 90;
integer swing_time = 5;
rotation rot_closed;

default
{
    state_entry()
    {
        rot_closed = llGetLocalRot();
    }
    
    touch_start(integer total_number)
    {
        if(open) return; // Ignore clicks while open.
        
        vector avatar_position = llDetectedPos(0);
        vector avatar_direction = llVecNorm(avatar_position - llGetPos()) / llGetRot();
        // llOwnerSay((string)avatar_direction);
        
        // Front face of the door is towards the outside.
        if( avatar_direction.x > 0 )
        {
            llOwnerSay("Welcome!");
            llSetLocalRot(llGetLocalRot() * llEuler2Rot(<0,0,swing_deg*DEG_TO_RAD>));
        }
        else
        {
            llOwnerSay("Goodbye!");
            llSetLocalRot(llGetLocalRot() * llEuler2Rot(<0,0,-swing_deg*DEG_TO_RAD>));
        }
        
        open = TRUE;
        llSetTimerEvent(swing_time);
    }
    
    timer()
    {
        llSetLocalRot(rot_closed);
        open = FALSE;
        llSetTimerEvent(0);
    }
}

The script won't need a reset even if the linkset is rotated. A reset is only needed if the door itself changes position or needs a new (local) closed rotation within the linkset.

Edited by Wulfie Reanimator
  • Like 1
  • Thanks 2
Link to comment
Share on other sites

  • 1 year later...
On 10/30/2017 at 5:45 AM, Wulfie Reanimator said:

This is what I came up with (works linked and unlinked, put the script directly into the door, door is assumed to be "cut in half" like Rolig's):

Brilliant!

been thinking about this funky implementation. how startling to have it freely given without asking for it. a million thanks. everyone should be using this! works for collision openings too.

the way-too-confusing bit is the prim cutting. the only reason for cutting the prim (if it acts as the door) is to cut away the excess prim area so the axis of the prim's rotation is at the remaining volume's edge. not necessary if you use the script in a 0.01m "hinge" for a complex door build. at this size, it's of negligible significance if it's cut or not. in this case the script goes in the root as the hinge/axis point.

definitely a must have script Wulfie!

Link to comment
Share on other sites

24 minutes ago, EnCore Mayne said:

Brilliant!

been thinking about this funky implementation. how startling to have it freely given without asking for it. a million thanks. everyone should be using this! works for collision openings too.

the way-too-confusing bit is the prim cutting. the only reason for cutting the prim (if it acts as the door) is to cut away the excess prim area so the axis of the prim's rotation is at the remaining volume's edge. not necessary if you use the script in a 0.01m "hinge" for a complex door build. at this size, it's of negligible significance if it's cut or not. in this case the script goes in the root as the hinge/axis point.

definitely a must have script Wulfie!

You're welcome.

It's not particularly difficult to have the script just rotate the door around an arbitrary point without an explicit hinge prim. I just followed Rolig's concept for simplicity's sake. 

Link to comment
Share on other sites

46 minutes ago, Wulfie Reanimator said:

It's not particularly difficult to have the script just rotate the door around an arbitrary point without an explicit hinge prim.

That's definitely true.  There are several different ways to script rotation around the visible or calculated "edge" of a door.  The most common ones are summarized in the sticky thread on doors at the top of this forum.  In pre-mesh days, the easiest solutions for many scripters were to either cut a door prim or slap an extra hinge prim on it.  These days, it is increasingly common to create a mesh door that has a tiny, transparent face as a built-in pivoting element.  As you say, though, it's always possible to calculate rotation around an axis that is offset from the door's center.

  • Like 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1856 days.

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

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...