////////// // Stackable Graph Cube script // // Author: Peter R. Bloomfield (SL: Pedro McMillan) // // Place this script inside several independent cubes. // You can then drag the cubes around, and they will 'snap' to each other neatly. // // // Note regarding object naming: // You can name the cubes whatever you like, but make sure all cubes which need // stack together have the SAME name. // // Note regarding scaling: // This is currently setup for a cube of size 0.5x0.5x0.5. // If you want to use a different scale, then change the "cubeSize" variable below. // Also update the "scanRange" variable to roughly double the width of a cube. // // // Known problems: // - can end up with 2 cubes occupying same space // - does not always detect when it has been moved // // Versions: // 1.0 - original version with physics... buggy and vertical stacking only // 1.1 - physics disabled by default, but cubes snap in all dimensions, with correct rotations // // ////////// // GPL: // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ////////// // What size are all cubes expected to be? vector cubeSize = <0.5, 0.5, 0.5>; // How far should we scan for other cubes? float scanRange = 1.0; // This will store the position after our last movement vector lastMove = <0.0, 0.0, 0.0>; // Should we use physics? // (Note: physics makes dragging cube around more fun, but is also prone to bugs) integer USE_PHYSICS = FALSE; default { state_entry() { // Whether we will use physics or not, disable it until we are moved llSetPrimitiveParams([PRIM_PHYSICS, FALSE]); } moving_start() { // Activate physics for as long as the cube is being moved if (USE_PHYSICS) llSetPrimitiveParams([PRIM_PHYSICS, TRUE]); } moving_end() { // Store our current position (in case the sensor is delayed) lastMove = llGetPos(); // Look around for other nearby graph cubes llSensor(llGetObjectName(), NULL_KEY, SCRIPTED | PASSIVE, scanRange, PI); // Deactivate physics so we don't get a feedback loop // (aligns self -> physics moves it slightly -> realigns self -> etc..) if (USE_PHYSICS) llSetPrimitiveParams([PRIM_PHYSICS, FALSE]); } sensor(integer num_detected) { // Go through each sensed object to find the nearest one integer i = 0; float nearestDist = -1.0; float curDist = 0.0; vector nearestPos = <0.0, 0.0, 0.0>; vector curPos = <0.0, 0.0, 0.0>; rotation nearestRot = ZERO_ROTATION; for (i = 0; i < num_detected; i++) { // Is it the nearest so far? curPos = llDetectedPos(i); curDist = llVecDist(curPos, lastMove); if (nearestDist < 0.0 || curDist < nearestDist) { nearestDist = curDist; nearestPos = curPos; nearestRot = llDetectedRot(i); } } // Did we end up finding a suitable cube nearby? if (nearestDist > 0.0) { // Calculate the vector between the sensed object and this one, // and translate it to local space for the sensed object // (this lets us figure out which side of it we're on) vector offset = (lastMove - nearestPos) / nearestRot; // Figure out which side of it we're on by checking which dimension different is biggest float xDiff = llFabs(offset.x); float yDiff = llFabs(offset.y); float zDiff = llFabs(offset.z); vector snapPos = <0.0, 0.0, 0.0>; // Determine the position we ought to be in, relative to the local // rotation of the object we are snapping to if (xDiff > yDiff) { if (xDiff > zDiff) { if (offset.x < 0.0) snapPos.x = -cubeSize.x; else snapPos.x = cubeSize.x; } else { if (offset.z < 0.0) snapPos.z = -cubeSize.z; else snapPos.z = cubeSize.z; } } else { if (yDiff > zDiff) { if (offset.y < 0.0) snapPos.y = -cubeSize.y; else snapPos.y = cubeSize.y; } else { if (offset.z < 0.0) snapPos.z = -cubeSize.z; else snapPos.z = cubeSize.z; } } // Apply our snap offset according to the detected rotation snapPos = nearestPos + (snapPos * nearestRot); llSetPrimitiveParams([PRIM_POSITION, snapPos, PRIM_ROTATION, nearestRot]); } } no_sensor() { // Nothing detected } }