Resizer multi-prims
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
All description in first comments of the script. Maybe my English is not so good (I'm French). Hope anyone will understand.
Resizer <lsl> /* =================================================================================== */ /* */ /* Resizer v1.2 */ /* */ /* License : */ /* Français */ /* OpenSource. Licence GPL 2 et plus. */ /* Modification, Copie et Transfert autorisés */ /* Sous condition de laisser le script en full perm (Modify / Copy / Transfer) */ /* */ /* English */ /* OpenSource. License GPL 2 and later. */ /* Modify, Copy and Transfer allowed if this script stay full perm */ /* */ /* Auteurs : Christy Mansbridge / Mingyar Ishtari, le 02 octobre 2010 */ /* */ /* Modifications / Updates : */ /* Author : */ /* Date : */ /* Detail : */ /* */ /* Usage : */ /* Français */ /* À placer dans le prim root (prim coutouré en jaune à l'édition) */ /* Cliquer l'objet pour obtention du menu bleu. */ /* Usage restreint au seul propriétaire. */ /* */ /* English */ /* Place the script in the root prim of the build. */ /* Click the object to get the blue menu. */ /* Usage restricted to the owner. */ /* */ /* Fonctions : */ /* Français */ /* 1) Script de redimensionnement d'un objet multi-prims respectant les */ /* proportions, en taille et en position, de chaque prim. */ /* */ /* 2) Lag minimal puisqu'un seul script par objet (1 à 256 prims) */ /* Possibilité offerte de supprimer le script. */ /* */ /* 3) Communication avec d'autres objets pour synchroniser le redimensionnement */ /* Pour permettre cette communication, plusieurs critères douvent être remplis*/ /* a) L'objet doit être porté ainsi que celui/ceux à synchroniser */ /* b) Les objets doivent porter le même nom, à l'exception de la partie */ /* finale séparée du reste par un espace. */ /* Exemple : Chaussure rouge gauche et Chaussure rouge droite */ /* */ /* 4) Garantie de la conservation de l'intégrité de l'objet. */ /* a) Si un agrandissement entraine une destruction du build, */ /* Retour automatique à la taille avant agrandissement. */ /* */ /* b) Si l'objet contient au moins un huge prim le script reste inactif. */ /* Cette sécurité résevée à la grille officielle SecondLife peut être */ /* désactivée pour opensim en passant à TRUE l'affectation de la variable */ /* globale iAllowResizeHugePrims. */ /* */ /* 5) Prise en compte automatique des changements de liens (ajout/suppresion de */ /* prims ou redimensionnement manuel) */ /* */ /* 6) 18 choix de redimensionnement prédéfinis en pourcentages : */ /* -1, -2, -5, -10, -25, -50, -75, -85, -95. */ /* +1, +2, +5, +10, +25, +50, +75, +100, +200, */ /* */ /* 7) 3 choix extrèmes : Dimensionnement Minimal, Maximal et par Défaut. */ /* La dimension par défaut est celle à l'initialisation du script ou */ /* celle affectée par le menu [Set default]. */ /* */ /* 8) Détermination de la taille maximale avant déformation de la construction */ /* Pour trouver la taille maximale possible, procéder ainsi : */ /* Effectuer des agrandissements à +200% jusqu'à refus de dimensionnement */ /* Répéter l'opération avec les pourcentages inférieurs, +100% puis +75% */ /* puis 50% ... jusqu'à répétition de l'opétation à +1%. */ /* Après un refus de redimensionnement à +1% le script connait la vraie */ /* taille maximale. */ /* */ /* English */ /* 1) Script to resize a multi prims object with conservation of its integrity */ /* (size and relative position of each prim). */ /* */ /* 2) Less lag : only 1 script for an object (1 to 256 prims) */ /* Possibility to remove the script by the menu. */ /* */ /* 3) Communication with other objects to synchronize resize */ /* To allow communication, objects have to comply with few criteria : */ /* a) The object has to be worn, objects to synchronize too. */ /* b) Objects have to have same name except last part separated by space */ /* Example : Excelsior shoes Left and Excelsior shoes Right */ /* */ /* 4) Integrity waranty. */ /* a) If after increasing size the build is broken, automatic go back to */ /* previous good size. */ /* */ /* b) If, at least, one huge prim is in the build, the script is inactive. */ /* This security is only for official grid SecondLife and may be bypassed */ /* for opensim, affecting TRUE to the iAllowResizeHugePrims global variable*/ /* */ /* 5) Automatic detection of manual scale change and link changes */ /* */ /* 6) 18 predefined resize choice in percentage : */ /* -1, -2, -5, -10, -25, -50, -75, -85, -95. */ /* +1, +2, +5, +10, +25, +50, +75, +100, +200, */ /* */ /* 7) 3 other choices : Resize to Minimum, Maximum or Default size. */ /* The default size is the size at initialisation of the script or the size */ /* affected by the blue menu [Set default]. */ /* */ /* 8) How to find the real maximum size ? */ /* To find the real value : */ /* Make an increase size by +200% until the script says it fails. */ /* Then, repeat this method with +100%, then after with +75% ... */ /* After the last increase with +1% the real maximum size is found and */ /* stored by the script. */ /* */ /* =================================================================================== */
integer iAllowResizeHugePrims = FALSE; // On opensim, affect TRUE integer iNumberOfPrims; integer iFirst; integer iLast; integer iBcl; integer iIdx; integer iDeb; integer iFin; integer iSens; float fTmp; vector vSize; vector vPos; float fMin; float fMax; float fFactor; float fDefault; integer iCanalComm = -8547548; integer iCanalMenu; integer iEcouteMenu; integer iMenuOn; integer iCurrentMenu; string sPctM1 = "1"; string sPctM2 = "2"; string sPctM3 = "5"; string sPctP1 = "1"; string sPctP2 = "2"; string sPctP3 = "5"; string sPct; integer iNbPasses; integer iHugeUsed; float fLimiteMax; integer iNoMoreMax; vector vPos1; integer iError; list lSortPrims; list lSizes; list lPos; list lElem; list lName;
// Hack to increase speed of the script (Mono) Booster() {
for( iBcl = 0; iBcl < 5000; iBcl++ );
}
// Function to get size and/or relative position of the current prim GetSizePos() {
vSize = llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_SIZE ] ), 0 ) * fFactor;
if( iBcl == iFirst ) { if( llGetAttached() )
// Root prim and object attached to the avatar, position is local
vPos = llGetLocalPos(); else
// Root prim and object not attached, position is absolute position
vPos = llGetRootPosition(); } else
// Child prim, position is relative to the root prim
vPos = ( ( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_POSITION ] ), 0 ) - llGetRootPosition() ) / llGetRootRotation() ) * fFactor;
}
// Function to control if current prim has expected size and relative position ControlSizePos( vector vRefSize, vector vRefPos ) {
if( llVecDist( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_SIZE ] ), 0 ), vRefSize ) > 0.001 ) iError = TRUE; else if( iBcl != iFirst ) { if( llVecDist( ( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_POSITION ] ), 0 ) - llGetRootPosition() ) / llGetRootRotation(), vRefPos ) > 0.001 ) iError = TRUE; }
}
// Function to test resize validity of the current prim, if resize is over stored maximum size integer TestError() {
if( fMax > fLimiteMax ) ControlSizePos( vSize, vPos );
return( iError );
}
// Function to control build integrity after resize ControlBuild( integer iBoost ) {
if( iBoost ) Booster();
// No individual error found // Control of all the build prim after prim
for( iIdx = iDeb; iIdx != iFin; iIdx += iSens ) { iBcl = llList2Integer( lSortPrims, iIdx );
ControlSizePos( llList2Vector( lSizes, iIdx ) * fFactor, llList2Vector( lPos, iIdx ) * fFactor );
if( iError ) { if( iBoost ) iIdx = iFin - iSens; return; } }
}
// Function to resize (only the size) each prim of the build ChangeSize( integer iCorrect ) {
Booster();
for( iIdx = iDeb; iIdx != iFin; iIdx += iSens ) { iBcl = llList2Integer( lSortPrims, iIdx ); GetSizePos();
if( iCorrect ) vPos /= fFactor;
llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_SIZE, vSize ] );
if( TestError() ) return; }
}
// Function to adapt relative position of each prim of the build ChangePos( integer iCorrect ) {
Booster();
for( iIdx = iDeb; iIdx != iFin; iIdx += iSens ) if( ( iBcl = llList2Integer( lSortPrims, iIdx ) ) != iFirst ) { GetSizePos(); vPos1 = vPos / fFactor;
if( iCorrect ) vSize /= fFactor;
llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_POSITION, vPos, PRIM_POSITION, vPos, PRIM_POSITION, vPos ] );
if( TestError() ) return; }
}
// Function to resize and adapt relative position of each prim of the build ChangeSizePos() {
Booster();
for( iIdx = iDeb; iIdx != iFin; iIdx += iSens ) { iBcl = llList2Integer( lSortPrims, iIdx ); GetSizePos();
llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_SIZE, vSize, PRIM_POSITION, vPos ] );
if( TestError() ) return; }
}
// Function to resize and position each prim to the previous stored good sizes and relative positions after an error GoBackToGoodSize() {
llOwnerSay( "Error, back to previous good size..." );
sPct = "aborted";
if( llRound( fFactor * 100.0 ) == 101 ) iNoMoreMax = TRUE;
fMin /= fFactor; fMax /= fFactor; iFin = iDeb - iSens; iDeb = iIdx; iSens *= -1; fFactor = 1.0; iNbPasses = 3;
if( iNoMoreMax ) fLimiteMax = fMax;
do { Booster(); iError = FALSE;
for( iIdx = iDeb; iIdx != iFin; iIdx += iSens ) { iBcl = llList2Integer( lSortPrims, iIdx );
lElem = [ PRIM_SIZE, llList2Vector( lSizes, iIdx ) ];
if( iBcl != iFirst ) { vPos = llList2Vector( lPos, iIdx );
lElem += [ PRIM_POSITION, vPos, PRIM_POSITION, vPos, PRIM_POSITION, vPos ]; }
llSetLinkPrimitiveParamsFast( iBcl, lElem ); }
ControlBuild( FALSE ); } while( iError && --iNbPasses );
if( iError ) llOwnerSay( "Sorry, the build is broken and can't be repaired." );
}
// Function to store size and relative positions of each prim after a successfull resizing SaveSizePos() {
Booster();
fFactor = 1.0; lSizes = []; lPos = [];
for( iIdx = 0; iIdx != iNumberOfPrims; iIdx++ ) { iBcl = llList2Integer( lSortPrims, iIdx ); GetSizePos();
lSizes += [ vSize ]; lPos += [ vPos ]; }
}
// Function to resize the build Resize( integer iMess ) { // Apply factor of resizing to the min and max size found in the build
fMin *= fFactor; fMax *= fFactor;
sPct = "done";
if( fFactor < 1.0 ) {
// If resize is to decrease the size, we start whith the most distant prims from the root
iDeb = llGetListLength( lSortPrims ) - 1; iFin = -1; iSens = -1; } else {
// If resize is to increase the size, we start whith the closer prims from the root
iDeb = 0; iFin = llGetListLength( lSortPrims ); iSens = 1; }
iError = FALSE;
if( fFactor < 0.5 )
// if decrease is more than a factor 2, we start by changing only relative positions
ChangePos( FALSE ); else if( fFactor > 2.0 )
// if increase is more than a factor 2, we start by changing only sizes
ChangeSize( FALSE ); else
// if factor is less or equal than 2, resize is both size and position
ChangeSizePos();
if( ! iError ) { if( fFactor < 0.5 )
// Changing positions is successfull, we change positions
ChangeSize( TRUE ); else if( fFactor > 2.0 )
// Changing sizes is successfull, we change sizes
ChangePos( TRUE ); }
if( ! iError ) ControlBuild( TRUE );
if( iError )
// Restore the last good size after error
GoBackToGoodSize(); else {
// Save the sizes and relative positions of each prim
SaveSizePos(); if( fMax == 10.0 && ! iAllowResizeHugePrims ) { iNoMoreMax = TRUE; fLimiteMax = fMax; } }
if( fMax > fLimiteMax )
// New max size found
fLimiteMax = fMax;
// Start countdown for CHANGED_SCALE event
llResetTime();
if( iMess ) {
// Inform user the resize is finished and call back the menu
llOwnerSay( "Resize " + sPct + "." ); Menu(); }
}
AlreadyAt( integer iMess, string sMess ) {
if( iMess ) { llOwnerSay( "Already at " + sMess + " size." ); Menu(); }
}
// Function to resize to the minimum size MinimumSize( integer iMess ) {
if( ( fFactor = 0.01 / fMin ) != 1.0 ) { if( iMess ) { sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 ); if( fFactor > 1.0 ) sPct = "+" + sPct; llOwnerSay( "Minimum size (" + sPct + "%)..." ); }
Resize( iMess ); } else AlreadyAt( iMess, "minimum" );
}
// Function to resize to the maximum size MaximumSize( integer iMess ) {
if( ( fFactor = fLimiteMax / fMax ) != 1.0 ) { if( iMess ) { sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 ); if( fFactor > 1.0 ) sPct = "+" + sPct; llOwnerSay( "Maximum size (" + sPct + "%)..." ); }
Resize( iMess ); } else AlreadyAt( iMess, "maximum" );
}
// Function to resize to the default size DefaultSize( integer iMess ) {
vSize = llList2Vector( llGetLinkPrimitiveParams( iFirst, [ PRIM_SIZE ] ), 0 );
if( ( fFactor = fDefault / vSize.x ) != 1.0 ) { if( iMess ) { sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 ); if( fFactor > 1.0 ) sPct = "+" + sPct; llOwnerSay( "Default size (" + sPct + "%)..." ); }
Resize( iMess ); } else AlreadyAt( iMess, "default" );
}
// Close the menu FinMenu( integer iAlert ) {
iMenuOn = FALSE; llSetTimerEvent( 0.0 ); llListenRemove( iEcouteMenu ); if( iAlert ) llOwnerSay( "Menu timeout." );
}
// Main menu Menu() {
llSetTimerEvent( 30.0 ); llDialog( llGetOwner(), "Resizer action :\n" + "Quit : Leave this menu.\n" + "Options : Current size becomes default.\n" + ">> : Next set of percentages.\n" + "Min : Resize to minimum size.\n" + "Default : Return to initial size.\n" + "Max : Resize to maximum size.", [ "Quit", "Options", ">>", "-" + sPctM1 + "%", "Min", "+" + sPctP1 + "%", "-" + sPctM2 + "%", "Default", "+" + sPctP2 + "%", "-" + sPctM3 + "%", "Max", "+" + sPctP3 + "%" ], iCanalMenu );
}
// Function to return min or max element of vSize vector float MinMaxVal( integer iOperation ) {
return( llListStatistics( iOperation, [ vSize.x, vSize.y, vSize.z ] ) );
}
// Initialisation of the script init() {
Booster();
iNumberOfPrims = llGetNumberOfPrims(); iFirst = (integer)( iNumberOfPrims > 1 ); iLast = iNumberOfPrims - (integer)( iNumberOfPrims == 1 );
llOwnerSay( "Searching min/max prim size..." );
fMin = 10.0; fMax = 0.01; fFactor = 1.0; for( iBcl = iFirst; ( fMax <= 10.0 || iAllowResizeHugePrims ) && iBcl <= iLast; iBcl++ ) { GetSizePos();
if( iBcl == iFirst ) { fDefault = vSize.x; lElem = [ 0.0, iFirst ]; } else lElem += [ llVecDist( ZERO_VECTOR, vPos ), iBcl ];
fTmp = MinMaxVal( LIST_STAT_MIN ); if( fTmp < fMin ) fMin = fTmp;
fTmp = MinMaxVal( LIST_STAT_MAX ); if( fTmp > fMax ) fMax = fTmp; }
if( fMax > 10.0 && ! iAllowResizeHugePrims ) { iHugeUsed = TRUE; llOwnerSay( "Script not active, object uses huge prim." ); } else { llOwnerSay( "Size range : " + (string)fMin + " -> " + (string)fMax );
iNoMoreMax = FALSE; iHugeUsed = FALSE;
llOwnerSay( "Sorting prims..." );
lElem = llListSort( lElem, 2, TRUE );
lSortPrims = []; for( iBcl = 1; iBcl < llGetListLength( lElem ); iBcl += 2 ) lSortPrims += [ llList2Integer( lElem, iBcl ) ];
llOwnerSay( (string)iNumberOfPrims + " prim" + llList2String( [ "", "s" ], (integer)(iNumberOfPrims > 1) ) + " sorted." );
fLimiteMax = llListStatistics( LIST_STAT_MAX, [ 10.0, fMax ] );
llOwnerSay( "Storing size/pos of each prim..." );
SaveSizePos(); }
llOwnerSay( "Init. complete." );
}
default {
state_entry() { init(); llListen( iCanalComm, "", NULL_KEY, "" ); }
changed( integer iChange ) { if( ( iChange & CHANGED_LINK ) || ( ( iChange & CHANGED_SCALE ) && llGetTime() > 5.0 / llList2Float( [ llGetRegionTimeDilation(), 0.01 ], (integer)( llGetRegionTimeDilation() < 0.01 ) ) ) ) init(); }
listen( integer iChannel, string sName, key kId, string sMess ) { if( iChannel == iCanalMenu ) { llSetTimerEvent( 0.0 );
if( sMess == ">>" ) { if( (++iCurrentMenu) == 3 ) iCurrentMenu = 0;
if( ! iCurrentMenu ) { sPctM1 = "1"; sPctM2 = "2"; sPctM3 = "5"; sPctP1 = "1"; sPctP2 = "2"; sPctP3 = "5"; } else if( iCurrentMenu == 1 ) { sPctM1 = "10"; sPctM2 = "25"; sPctM3 = "50"; sPctP1 = "10"; sPctP2 = "25"; sPctP3 = "50"; } else { sPctM1 = "75"; sPctM2 = "85"; sPctM3 = "95"; sPctP1 = "75"; sPctP2 = "100"; sPctP3 = "200"; }
Menu(); } else if( sMess == "Options" || sMess == " " ) { llSetTimerEvent( 30.0 ); llDialog( llGetOwner(), "\nResizer options :\n \n" + "Quit : Leave this menu.\n" + "BACK : To main menu.\n" + "Set default : Current size is default.\n" + "Remove : Remove the script.", [ "Quit", " ", "BACK", "Set default", " ", "Remove" ], iCanalMenu ); } else if( sMess == "BACK" ) Menu(); else if( sMess == "Set default" ) { vSize = llList2Vector( llGetLinkPrimitiveParams( iFirst, [ PRIM_SIZE ] ), 0 ); fDefault = vSize.x;
if( llGetAttached() ) llSay( iCanalComm, (string)llGetOwner() + "|Default|" + (string)fDefault );
llOwnerSay( "This size is now the default size." ); Menu(); } else if( sMess == "Remove" ) { llSetTimerEvent( 30.0 ); llDialog( llGetOwner(), "\nSure to remove the script from this object ?", [ "Yes", "No" ], iCanalMenu ); } else if( sMess == "Default" ) DefaultSize( TRUE ); else if( sMess == "Min" ) MinimumSize( TRUE ); else if( sMess == "Max" ) MaximumSize( TRUE ); else if( ( iBcl = llListFindList( [ "+", "-" ], [ llGetSubString( sMess, 0, 0 ) ] ) ) >= 0 ) { fFactor = 1.0 + (float)llGetSubString( sMess, 0, -2 ) / 100.0; fTmp = llList2Float( [ llList2Float( [ 10.0, 10000.0 ], iAllowResizeHugePrims ), fLimiteMax ], iNoMoreMax );
if( fMin * fFactor < 0.01 ) fFactor = 0.01 / fMin; else if( fMax * fFactor > fTmp ) fFactor = fTmp / fMax;
if( fMin * fFactor >= 0.01 && fMax * fFactor <= fTmp && llRound( fFactor * 100.0 ) != 100 ) { sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 ) + "%"; if( fFactor > 1.0 ) sPct = "+" + sPct; llOwnerSay( llList2String( [ "Increas", "Reduc" ], iBcl ) + "ing size : " + sPct + llList2String( [ " (" + sMess + " asked)", "" ], (integer)(sMess == sPct) ) + "..." );
if( llGetAttached() ) llSay( iCanalComm, (string)llGetOwner() + "|Factor|" + (string)fFactor );
Resize( TRUE ); } else { llOwnerSay( "Out of size range." ); Menu(); } } else { FinMenu( FALSE );
if( sMess == "Yes" ) { if( llGetAttached() ) llSay( iCanalComm, (string)llGetOwner() + "|Remove" ); llRemoveInventory( llGetScriptName() ); } } } else if( llGetAttached() && sName != llGetObjectName() ) { lElem = llParseString2List( sName, [ " " ], [ "" ] ); if( llGetListLength( lElem ) < 2 ) return; lName = llParseString2List( llGetObjectName(), [ " " ], [ "" ] ); if( llGetListLength( lName ) < 2 ) return;
if( llDumpList2String( llList2List( lElem, 0, -2 ), " " ) != llDumpList2String( llList2List( lName, 0, -2 ), " " ) ) return;
lElem = llParseString2List( sMess, [ "|" ], [ "" ] );
if( llList2String( lElem, 0 ) == (string)llGetOwner() ) { if( llList2String( lElem, 1 ) == "Remove" ) llRemoveInventory( llGetScriptName() ); else if( llList2String( lElem, 1 ) == "Factor" ) { fFactor = llList2Float( lElem, 2 ); Resize( FALSE );
llResetTime(); } else if( llList2String( lElem, 1 ) == "Min" ) MinimumSize( FALSE ); else if( llList2String( lElem, 1 ) == "Max" ) MaximumSize( FALSE ); else if( llList2String( lElem, 1 ) == "Default" ) { if( llGetListLength( lElem ) == 2 ) DefaultSize( FALSE ); else fDefault = llList2Float( lElem, 2 ); } } } }
timer() { FinMenu( TRUE ); }
touch_start( integer iNum ) { if( llDetectedKey( 0 ) == llGetOwner() && ! iMenuOn && ! iHugeUsed ) {
//llOwnerSay( (string)llGetFreeMemory() );
iMenuOn = TRUE; iEcouteMenu = llListen( iCanalMenu = iCanalComm - 10 - (integer)llFrand( 124578 ), "", llGetOwner(), "" ); Menu(); } }
} </lsl>