# A Bi-Swing Door that opens Away from you

## 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);
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);
}
}```

• 5
• 1

##### Share on other sites

I'm not sure what I'm doing wrong. I didn't change anything in this script. I just installed it in my closed door. I tested it by clicking, walking through, letting it close. Then clicking it again. It opens in the same direction no matter which side of the door I'm on

##### Share on other sites

@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*.

##### Share on other sites
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

##### Share on other sites

Ah OK. Sorry. Too late :D. When I have seen those they swung to and fro but they still didn't change direction. Rolig will answer your question when she is on no doubt. Good luck.

##### 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.

##### Share on other sites

Yep, I used it on both. I also put in an owner say to see what WhereAmI was returning. Unlinked, it was always returning 0. Linked (yes as a child), is was always returning 1

##### Share on other sites

That's odd.  Until I am back in the land of reliable Internet, I can only speculate, and I'm not speculating well so far.

##### 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

##### Share on other sites

Well, that's certainly simpler than my math, although I think the two should be equivalent. Dora and I seem to be using the same approach.  The bottom line, though, is that if the simpler math works, use it!  Simpler is always preferable.  Thanks, @Ruthven Willenov

##### Share on other sites

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);
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);
}
}```

##### Share on other sites

Nice idea!  Thanks, Ruthven.

##### Share on other sites

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!"

##### Share on other sites

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!");
}
else
{
llOwnerSay("Goodbye!");
}

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

##### Share on other sites
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!

##### 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.

##### 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.

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.