null nil 0 0 0 1 0 0 0 1 0 0 0 1 PrecisionDragger null false Precision 0 then if not rotateAdornPart then rotateAdornPart = Instance.new("Part", game.CoreGui) rotateAdornPart.Name = "RotateAdornPart" end local filteredSelectionMetaPart = Selection.getFilteredSelectionMetapart() rotateAdornPart.CFrame = filteredSelectionMetaPart.CFrame rotateAdornPart.Size = filteredSelectionMetaPart.Size Adorn.adornInstanceWithRotate(rotateAdornPart) end end local function updateAdornments() local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if filteredSelectionMetapart then filteredSelectionMetapart.ClearCache() end updateInvisiblePart() updateRotatePart() end local function onPressMouse() Adorn.grabHandle() local currentHandle = Adorn.getCurrentHandle() if currentHandle == H_NONE and not Adorn.isPlaneSelectingModeOn() then selectPart() end if currentHandle == H_PLANE then initialPos = mouse.Origin.p else local currentAdorn = Adorn.getCurrentAdornment() if currentAdorn then initialPos = Adorn.getAdornmentWorldCFrame(Adorn.getCurrentAdornment()[1]).p end end grabHandle(currentHandle, initialPos) end local function onReleaseMouse() initialPos = nil if not RubberBand.isRubberBandDragInProgress() then releaseHandle() end releasePart() Adorn.releaseHandle() planeDragging = false if planeObject and false then planeObject:Destroy() planeObject = nil end end ------------------------------------------------- toolbarbutton.Click:connect(function() if on and Off then Off() elseif loaded and On then On() end end) plugin.Deactivation:connect(function() if on and Off then Off() end end) --------------------MATH STUFFS------------------- function squaredMagnitude(vect3) return vect3.X*vect3.X + vect3.Y*vect3.Y + vect3.Z*vect3.Z end function vector3Direction(vect3) local lenSquared = squaredMagnitude(vect3) local invSqrt = 1.0 / math.sqrt(lenSquared) return Vector3.new(vect3.X * invSqrt, vect3.Y * invSqrt, vect3.Z *invSqrt) end function vector3LessThanOrEqualTo(vect1, vect2) return vect1.x <= vect2.x and vect1.y <= vect2.y and vect1.z <= vect2.z end function vector3GreaterThanOrEqualTo(vect1, vect2) return vect1.x >= vect2.x and vect1.y >= vect2.y and vect1.z >= vect2.z end function createPlane(vector0, vector1, vector2) local p1 = vector1 - vector0 local p2 = vector2 - vector0 local cross = p1:Cross(p2) local _normal = vector3Direction(cross) local _distance = _normal:Dot(vector0) return {v0=vector0, v1=vector1, v2=vector2, normal=_normal, distance=_distance} end function rayPlaneIntersection(ray, plane) local dotProd = ray.Direction:Dot(plane.normal) local t = -((-plane.distance) + ray.Origin:Dot(plane.normal)) / dotProd return ray.Origin + ray.Direction * t end function boxSideTest(ray, normal, size, p) local origin = size * normal local t = (-((-normal:Dot(origin)) + ray.Origin:Dot(normal))) / ray.Direction:Dot(normal) local hit = ray.Origin + ray.Direction * t if (origin.x ~= 0 or hit.x <= size.x) and (origin.x ~= 0 or hit.x >= -size.x) and (origin.y ~= 0 or hit.y <= size.y) and (origin.y ~= 0 or hit.y >= -size.y) and (origin.z ~= 0 or hit.z <= size.z) and (origin.z ~= 0 or hit.z >= -size.z) then return hit end return nil end function rayBoxIntersection(ray, cframe, size) size = size / 2 local localRay = Ray.new(cframe:pointToObjectSpace(ray.Origin), cframe:pointToObjectSpace(ray.Direction + cframe.p).unit ) if (localRay.Origin.x < -size.x) and (localRay.Direction.x > 0) then local result = boxSideTest(localRay, Vector3.new(-1, 0, 0), size) if result then return cframe:pointToWorldSpace(result) end elseif (localRay.Origin.x > size.x) and (localRay.Direction.x < 0) then local result = boxSideTest(localRay, Vector3.new(1, 0, 0), size, true) if result then return cframe:pointToWorldSpace(result) end end if (localRay.Origin.y < -size.y) and (localRay.Direction.y > 0) then local result = boxSideTest(localRay, Vector3.new(0, -1, 0), size) if result then return cframe:pointToWorldSpace(result) end elseif (localRay.Origin.y > size.y) and (localRay.Direction.y < 0) then local result = boxSideTest(localRay, Vector3.new(0, 1, 0), size) if result then return cframe:pointToWorldSpace(result) end end if (localRay.Origin.z < -size.z) and (localRay.Direction.z > 0) then local result = boxSideTest(localRay, Vector3.new(0, 0, -1), size) if result then return cframe:pointToWorldSpace(result) end elseif (localRay.Origin.z > size.z) and (localRay.Direction.z < 0) then local result = boxSideTest(localRay, Vector3.new(0, 0, 1), size) if result then return cframe:pointToWorldSpace(result) end end return cframe:pointToWorldSpace(Vector3.new(0,0,0)) end function projectVectorToPlane(vect, planeNormal) return vect - (vect:Dot(planeNormal.Unit) * planeNormal.Unit) end --- Collision --- local function moveUntilCollideWrapper(part, threshold, ignoreList, direction, maximum) if not ignoreList then ignoreList = {} end return Collision.moveUntilCollide(part, ignoreList, direction, threshold, maximum) end local function rayCastWithWhiteList(ray, whiteList) local ignoreList = List.createIgnoreListGivenWhiteList(game.Workspace, ignoreList) game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList) end local function safeMoveWrapper(part, threshold, ignoreList, direction) Collision.SafeMove(part, ignoreList, direction) end local function safeMoveWhiteList(part, direction, whiteList) Collision.SafeMove(part, List.createIgnoreListGivenWhiteList(game.Workspace, whiteList), direction) end local function safeRotate(part, previousChange) --TODO: Actual safe rotate part.CFrame = (previousChange) * part.CFrame end --- Get Grid --- function getRotationalIntervalFromGrid() local grid = plugin.GridSize if FuzzyMath.fuzzyCompare(grid, 0.2) then return 15 elseif FuzzyMath.fuzzyCompare(grid, 0.01) then return 1 else return 45 end end ---Round Num --- function roundToNearestGrid(value) local interval = plugin.GridSize if interval ~= 0 then return Round.roundToNearest(value, interval) end return value end function Vector3ToNearestGrid(value) return Vector3.new( roundToNearestGrid(value.X), roundToNearestGrid(value.Y), roundToNearestGrid(value.Z)) end function getScaleHandleLocalVector(handle) if handle == S_X_POS then return Vector3.new(1, 0, 0) elseif handle == S_X_NEG then return Vector3.new(-1, 0, 0) elseif handle == S_Z_POS then return Vector3.new(0, 0, 1) elseif handle == S_Z_NEG then return Vector3.new(0, 0, -1) elseif handle == S_Y_POS then return Vector3.new(0, 1, 0) --elseif handle == S_Y_NEG then -- return Vector3.new(0, -1, 0) elseif handle == S_X_POS_Z_POS then return Vector3.new(1, 0, 1) elseif handle == S_X_POS_Z_NEG then return Vector3.new(1, 0, -1) elseif handle == S_X_NEG_Z_POS then return Vector3.new(-1, 0, 1) elseif handle == S_X_NEG_Z_NEG then return Vector3.new(-1, 0, -1) end return Vector3.new(0, 0, 0) end function snapVector3ByHandle(value, handle) if handle == S_X_POS or handle == S_X_NEG or handle == S_X_POS_Z_POS or handle == S_X_POS_Z_NEG or handle == S_X_NEG_Z_POS or handle == S_X_NEG_Z_NEG then value = Vector3.new(math.max(roundToNearestGrid(value.X), plugin.GridSize), value.Y, value.Z) end if handle == S_Z_POS or handle == S_Z_NEG or handle == S_X_POS_Z_POS or handle == S_X_POS_Z_NEG or handle == S_X_NEG_Z_POS or handle == S_X_NEG_Z_NEG then value = Vector3.new(value.X, value.Y, math.max(roundToNearestGrid(value.Z), plugin.GridSize)) end if handle == S_Y_POS or --handle == S_Y_NEG or handle == T_Y_POS then value = Vector3.new(value.X, math.max(roundToNearestGrid(value.Y), plugin.GridSize), value.Z) end return value end -------------------------------------------------- function getSelectedPart() local selectedItems = Selection.getFilteredSelection() if (#selectedItems < 1) then return nil end return selectedItems[1] end function getCurrentSelectionWithChildren(children, t) if not t then t = {} end if not children then children = Selection.getFilteredSelection() end for i,v in pairs(children) do if v:IsA("BasePart") then table.insert(t, v) end if #v:GetChildren() then t = getCurrentSelectionWithChildren(v:GetChildren(), t) end end return t end function setPartPosition(part, position) part.CFrame = part.CFrame - part.CFrame.p + position end function setPartRotation(part, cframe) part.CFrame = cframe - cframe.p + part.CFrame.p end ------------------------------------------------ function cosineSimilarity(v1, v2) local value = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/ (math.sqrt(math.pow(v1.x,2) + math.pow(v1.y, 2) + math.pow(v1.z, 2)) * math.sqrt(math.pow(v2.x,2) + math.pow(v2.y, 2) + math.pow(v2.z, 2))) local radAngle = math.acos(value) return math.deg(radAngle) end function setWaypoint() removeDragPart() local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() local currentHandle = Adorn.getCurrentHandle() local handleType = "Unknown" if currentHandle == S_X_POS or currentHandle == S_Y_POS or --currentHandle == S_Y_NEG or currentHandle == S_Z_POS or currentHandle == S_X_NEG or currentHandle == S_Z_NEG or currentHandle == S_X_POS_Z_POS or currentHandle == S_X_NEG_Z_POS or currentHandle == S_X_NEG_Z_NEG or currentHandle == S_X_POS_Z_NEG then handleType = "Scale" elseif currentHandle == T_Y_POS then handleType = "Move" elseif currentHandle == R_XY or currentHandle == R_XZ or currentHandle == R_YZ then handleType = "Rotate" end if filteredSelectionMetapart and (filteredSelectionMetapart.CFrame ~= originalPosition or filteredSelectionMetapart.Size ~= originalSize) then game:GetService("ChangeHistoryService"):SetWaypoint(handleType) end end local function setSelection(newSelection) game:GetService("Selection"):Set(newSelection) end function rotateCFrame(cframe, side) if side == BOTTOM_SIDE then cframe = cframe * CFrame.Angles(math.rad(180), 0, 0) elseif side == RIGHT_SIDE then cframe = cframe * CFrame.Angles(0, 0, -math.rad(90)) elseif side == LEFT_SIDE then cframe = cframe * CFrame.Angles(0, 0, math.rad(90)) elseif side == FRONT_SIDE then cframe = cframe * CFrame.Angles(-math.rad(90), 0, 0) elseif side == BACK_SIDE then cframe = cframe * CFrame.Angles(math.rad(90), 0, 0) end return cframe end function updateInvisiblePart() if not invisiblePart then return end local selection = Selection.getFilteredSelection() if #selection < 1 then return end if #selection > 1 then end local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if secondaryPart then if filteredSelectionMetapart then filteredSelectionMetapart.UpdatePlaneCFrame = workplaneFrame invisiblePart.CFrame = filteredSelectionMetapart.PlaneAlignedCFrame invisiblePart.Size = filteredSelectionMetapart.PlaneAlignedSize else local tmpCFrame = secondaryPart.CFrame invisiblePart.CFrame = CFrame.new(invisiblePart.CFrame.p) Extent.setPartCFrameToExtents(invisiblePart, rotateCFrame(tmpCFrame, secondaryPartSideSelected)) end else Extent.setPartCFrameToExtents(invisiblePart, nil) end if secondaryLocation then workplaneOffset = -invisiblePart.CFrame:pointToObjectSpace(secondaryLocation).y end end local function resetDragger() Adorn.resetDragger() end ------------------------------------------------------------------------------------------ ------------------------------Adornment Drag Part Update---------------------------------- ------------------------------------------------------------------------------------------ local rotationalDiff = 0 local isInsideRotate = false local intersectionPoint = nil --TODO:: lots of copy pasta here, please consolidate function preUpdatePart() --calculateCurrentDistance local currentHandle = Adorn.getCurrentHandle() if currentHandle == H_NONE then return end if not initialPos then return end if currentHandle == H_PLANE then return end local currentAdornment = Adorn.getCurrentAdornment() local adornee = currentAdornment[1].Adornee if not adornee then return end local worldCFrame = Adorn.getAdornmentWorldCFrame(currentAdornment[1]) local lookVector = mouse.UnitRay if currentHandle == T_Y_POS or currentHandle == S_Y_POS then local upVector = (adornee.CFrame:pointToWorldSpace(Vector3.new(0, 1, 0)) - adornee.CFrame.p).Unit local planeNormal = lookVector.Direction - (lookVector.Direction:Dot(upVector) * upVector) local dist = planeNormal:Dot(worldCFrame.p) local dotProd = lookVector.Direction:Dot(planeNormal) local t = -((-dist) + lookVector.Origin:Dot(planeNormal)) / dotProd local intersection = lookVector.Origin + lookVector.Direction * t local origin = initialPos - (upVector.Unit * 800) local point = Ray.new(initialPos , upVector.Unit):ClosestPoint(intersection) local upV = Vector3.new(0, 1, 0) if point == initialPos then point = Ray.new(initialPos , -upVector.Unit):ClosestPoint(intersection) upV = upV * -1 end currentDist = (point - initialPos).Magnitude * upV elseif currentHandle == S_X_POS or currentHandle == S_X_NEG then local moveVector = (adornee.CFrame:pointToWorldSpace(Vector3.new(1, 0, 0)) - adornee.CFrame.p).Unit local planeNormal = lookVector.Direction - (lookVector.Direction:Dot(moveVector) * moveVector) local dist = planeNormal:Dot(worldCFrame.p) local dotProd = lookVector.Direction:Dot(planeNormal) local t = -((-dist) + lookVector.Origin:Dot(planeNormal)) / dotProd local intersection = lookVector.Origin + lookVector.Direction * t local origin = initialPos - (moveVector.Unit * 800) local point = Ray.new(initialPos , moveVector.Unit):ClosestPoint(intersection) local upV = Vector3.new(1, 0, 0) if point == initialPos then point = Ray.new(initialPos , -moveVector.Unit):ClosestPoint(intersection) upV = upV * -1 end currentDist = (point - initialPos).Magnitude * upV if currentHandle == S_X_NEG then currentDist = currentDist * -1 end elseif currentHandle == S_Z_POS or currentHandle == S_Z_NEG then local moveVector = (adornee.CFrame:pointToWorldSpace(Vector3.new(0, 0, 1)) - adornee.CFrame.p).Unit local planeNormal = lookVector.Direction - (lookVector.Direction:Dot(moveVector) * moveVector) local dist = planeNormal:Dot(worldCFrame.p) local dotProd = lookVector.Direction:Dot(planeNormal) local t = -((-dist) + lookVector.Origin:Dot(planeNormal)) / dotProd local intersection = lookVector.Origin + lookVector.Direction * t local origin = initialPos - (moveVector.Unit * 800) local point = Ray.new(initialPos , moveVector.Unit):ClosestPoint(intersection) local upV = Vector3.new(0, 0, 1) if point == initialPos then point = Ray.new(initialPos , -moveVector.Unit):ClosestPoint(intersection) upV = upV * -1 end currentDist = (point - initialPos).Magnitude * upV if currentHandle == S_Z_NEG then currentDist = currentDist * -1 end elseif currentHandle == S_X_POS_Z_POS or currentHandle == S_X_NEG_Z_NEG or currentHandle == S_X_POS_Z_NEG or currentHandle == S_X_NEG_Z_POS then local planeNormal = (adornee.CFrame:pointToWorldSpace(Vector3.new(0, 1, 0)) - adornee.CFrame.p).Unit local dist = planeNormal:Dot(worldCFrame.p) local dotProd = lookVector.Direction:Dot(planeNormal) local t = -((-dist) + lookVector.Origin:Dot(planeNormal)) / dotProd local intersection = lookVector.Origin + lookVector.Direction * t currentDist = originalCFrame:pointToObjectSpace(intersection + originalCFrame.p) - originalCFrame:pointToObjectSpace(initialPos + originalCFrame.p) if currentHandle == S_X_NEG_Z_POS or currentHandle == S_X_NEG_Z_NEG then currentDist = currentDist * Vector3.new(-1, 1, 1) end if currentHandle == S_X_POS_Z_NEG or currentHandle == S_X_NEG_Z_NEG then currentDist = currentDist * Vector3.new(1, 1, -1) end elseif currentHandle == R_XY or currentHandle == R_XZ or currentHandle == R_YZ then local planeNormal = (adornee.CFrame:pointToWorldSpace(Vector3.new(currentHandle == R_YZ and 1 or 0, currentHandle == R_XZ and 1 or 0, currentHandle == R_XY and 1 or 0)) - adornee.CFrame.p).Unit local dist = planeNormal:Dot(worldCFrame.p) local dotProd = lookVector.Direction:Dot(planeNormal) local t = -((-dist) + lookVector.Origin:Dot(planeNormal)) / dotProd intersectionPoint = lookVector.Origin + lookVector.Direction * t currentDist = originalCFrame:pointToObjectSpace(intersectionPoint + originalCFrame.p) - originalCFrame:pointToObjectSpace(initialPos + originalCFrame.p) isInsideRotate = false end if not mouseOffsetOnGrabHandle then mouseOffsetOnGrabHandle = currentDist else currentDist = currentDist - mouseOffsetOnGrabHandle end end local planePreviouslySelected = false local itemToUpdate = nil local sanitizationPrecision = 1000000 local function sanitizeFloatTest(value) return value > 0.0 and (math.ceil((value * sanitizationPrecision) - 0.5) / sanitizationPrecision) or (math.floor((value * sanitizationPrecision) + 0.5) / sanitizationPrecision) end local function sanitizeCFrameTest(value) local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = value:components() return CFrame.new( sanitizeFloat(x), sanitizeFloat(y), sanitizeFloat(z), sanitizeFloat(r00), sanitizeFloat(r01), sanitizeFloat(r02), sanitizeFloat(r10), sanitizeFloat(r11), sanitizeFloat(r12), sanitizeFloat(r20), sanitizeFloat(r21), sanitizeFloat(r22)) end function updatePart() local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() local filteredSelection = Selection.getFilteredSelection() local currentHandle = Adorn.getCurrentHandle() if currentHandle == H_NONE then return end if not setAnchoredStateForMovingParts then local selection = getCurrentSelectionWithChildren() setAnchoredStateForMovingParts = true for i, v in ipairs(selection) do states[i] = v.Anchored v.Anchored = true end end preUpdatePart() if ((not originalSize or not originalCFrame) and currentHandle ~= T_Y_POS or not currentDist) then if currentHandle ~= R_XY and currentHandle ~= R_XZ and currentHandle ~= R_YZ and currentHandle ~= H_PLANE then return end end local allowAdornUpdate = true if selectedPart and shouldBreakJoints then UnjoinSelection() --BreakJoints(selectedPart) end local refreshDragPart = true local refreshInvisiblePart = true local preactionCFrame local preActionSize if selectedPart and not itemToUpdate then preactionCFrame = selectedPart.CFrame preActionSize = selectedPart.Size end handleWasDragged = true local selection = Selection.getFilteredSelection() if #selection == 0 then resetDragger() return end local isPreviouslyColliding = false if plugin.CollisionEnabled then invisiblePart.Parent = workspace if List.itemsHasItemNotInList(invisiblePart:GetTouchingParts(), selection) then isPreviouslyColliding = true end invisiblePart.Parent = nil end if currentHandle == S_Y_POS then local yScale = -Adorn.getYScale() currentDist = currentDist * -yScale selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(yScale * ((originalSize - selectedPart.Size) / 2))) local totalDist = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local diff = (preactionCFrame.p - selectedPart.CFrame.p).unit if plugin.CollisionEnabled and #selectedPart:GetTouchingParts() > 0 then selectedPart.Size = preActionSize if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame if isPreviouslyColliding then return end moveUntilCollideWrapper(selectedPart, 0.00001, nil, diff * -1, totalDist * 2) local distanceMoved = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local positionDiff = preactionCFrame.p - selectedPart.CFrame.p selectedPart.Size = preActionSize - (distanceMoved * getScaleHandleLocalVector(currentHandle) * yScale) if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame - (positionDiff / 2) end local normal = selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(0, 1, 0)) - selectedPart.CFrame.p local cameraDirection = game.Workspace.Camera.CoordinateFrame.lookVector local projectedCameraDirection = cameraDirection - (cameraDirection:Dot(normal) * normal) local tangent = projectedCameraDirection:Cross(normal).unit Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(0, -1, 0)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(0, 1, 0)), tangent) elseif currentHandle == S_X_POS or currentHandle == S_Z_POS then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end if (selectedPart.FormFactor == Enum.FormFactor.Symmetric) and (selectedPart.Shape == Enum.PartType.Ball or selectedPart.Shape == Enum.PartType.Cylinder) then selectedPart.Size = snapVector3ByHandle(originalSize + Utility.customToSymmetric(currentDist), currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new((originalSize - selectedPart.Size) * 0.5 * Vector3.new(currentHandle == S_X_POS and -1 or 0, -1, currentHandle == S_Z_POS and -1 or 0))) else selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) * 0.5))) end local totalDist = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local diff = (preactionCFrame.p - selectedPart.CFrame.p).unit if plugin.CollisionEnabled and #selectedPart:GetTouchingParts() > 0 then selectedPart.Size = preActionSize if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame if isPreviouslyColliding then return end moveUntilCollideWrapper(selectedPart, 0.00001, nil, diff * -1, totalDist * 2) local distanceMoved = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local positionDiff = preactionCFrame.p - selectedPart.CFrame.p selectedPart.Size = preActionSize + (distanceMoved * getScaleHandleLocalVector(currentHandle)) if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame - (positionDiff / 2) end if currentHandle == S_X_POS then local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) elseif currentHandle == S_Z_POS then local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) end -- filteredSelectionMetapart.Size = selectedPart.Size -- filteredSelectionMetapart.CFrame = selectedPart.CFrame elseif currentHandle == S_X_NEG or currentHandle == S_Z_NEG then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end if (selectedPart.FormFactor == Enum.FormFactor.Symmetric) and (selectedPart.Shape == Enum.PartType.Ball or selectedPart.Shape == Enum.PartType.Cylinder) then selectedPart.Size = snapVector3ByHandle(originalSize + Utility.customToSymmetric(currentDist), currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new((originalSize - selectedPart.Size) * 0.5 * Vector3.new(currentHandle == S_X_NEG and 1 or 0, -1, currentHandle == S_Z_NEG and 1 or 0))) else selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(((originalSize - selectedPart.Size) * 0.5))) end local totalDist = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local diff = (preactionCFrame.p - selectedPart.CFrame.p).unit if plugin.CollisionEnabled and #selectedPart:GetTouchingParts() > 0 then selectedPart.Size = preActionSize if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame if isPreviouslyColliding then return end moveUntilCollideWrapper(selectedPart, 0.00001, nil, diff * -1, totalDist * 2) local distanceMoved = Utility.distanceVector3(preactionCFrame.p, selectedPart.CFrame.p) local positionDiff = preactionCFrame.p - selectedPart.CFrame.p selectedPart.Size = preActionSize - (distanceMoved * getScaleHandleLocalVector(currentHandle)) if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.CFrame = preactionCFrame - (positionDiff / 2) end if currentHandle == S_X_NEG then local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) elseif currentHandle == S_Z_NEG then local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) end elseif currentHandle == S_X_POS_Z_POS then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2))) local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) Adorn.scaleTwo(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) elseif currentHandle == S_X_NEG_Z_POS then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end if (selectedPart.FormFactor == Enum.FormFactor.Symmetric) and (selectedPart.Shape == Enum.PartType.Ball or selectedPart.Shape == Enum.PartType.Cylinder) then selectedPart.Size = snapVector3ByHandle(originalSize + Utility.customToSymmetric(currentDist), currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 1, 1))) else selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 0, 1))) end local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) Adorn.scaleTwo(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) elseif currentHandle == S_X_NEG_Z_NEG then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end if (selectedPart.FormFactor == Enum.FormFactor.Symmetric) and (selectedPart.Shape == Enum.PartType.Ball or selectedPart.Shape == Enum.PartType.Cylinder) then selectedPart.Size = snapVector3ByHandle(originalSize + Utility.customToSymmetric(currentDist), currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 1, -1))) else selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 0, -1))) end local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) Adorn.scaleTwo(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) elseif currentHandle == S_X_POS_Z_NEG then if shouldBreakJoints then UnjoinSelection() BreakJoints(selectedPart) end if (selectedPart.FormFactor == Enum.FormFactor.Symmetric) and (selectedPart.Shape == Enum.PartType.Ball or selectedPart.Shape == Enum.PartType.Cylinder) then selectedPart.Size = snapVector3ByHandle(originalSize + Utility.customToSymmetric(currentDist), currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(1, 1, -1))) else selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle) selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(1, 0, -1))) end local localCamera = selectedPart.CFrame:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p) local side = Utility.getVector3Sign(localCamera) Adorn.scaleOne(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(-1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(1, -1, side.z)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(0, 0, 1)) - selectedPart.CFrame.p) Adorn.scaleTwo(selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, -1)), selectedPart.CFrame:pointToWorldSpace(selectedPart.Size / 2 * Vector3.new(side.x, -1, 1)), selectedPart.CFrame:pointToWorldSpace(side * Vector3.new(1,0,0)) - selectedPart.CFrame.p) elseif currentHandle == T_Y_POS then local previousPosition = invisiblePart.CFrame invisiblePart.CFrame = previousAABBCFrame:toWorldSpace(CFrame.new(currentDist)) local offset1 = -invisiblePart.CFrame:pointToObjectSpace(secondaryLocation).y - invisiblePart.Size.Y / 2 local offset2 = -invisiblePart.CFrame:pointToObjectSpace(secondaryLocation).y + invisiblePart.Size.Y / 2 local snappedOffset1 = roundToNearestGrid(offset1) local snappedOffset2 = roundToNearestGrid(offset2) local deltaOffset1 = snappedOffset1 - offset1 local deltaOffset2 = snappedOffset2 - offset2 local deltaOffset = math.abs(deltaOffset1) <= math.abs(deltaOffset2) and deltaOffset1 or deltaOffset2 local worldOffset = invisiblePart.CFrame:pointToWorldSpace(Vector3.new(0, deltaOffset, 0)) invisiblePart.CFrame = invisiblePart.CFrame - invisiblePart.CFrame.p + worldOffset; local initialTest = false if plugin.CollisionEnabled then local positionDiff = invisiblePart.CFrame.p - previousPosition.p local totalDist = Utility.distanceVector3(previousPosition.p, invisiblePart.CFrame.p) if filteredSelectionMetapart.CanSimulate then filteredSelectionMetapart.TranslateFromTo(previousPosition, invisiblePart.CFrame) if List.itemsHasItemNotInList(filteredSelectionMetapart:GetTouchingParts(), filteredSelectionMetapart.Children) then local unitDiff = positionDiff.Unit filteredSelectionMetapart.TranslateFromTo(invisiblePart.CFrame, previousPosition) --move until collide Collision.moveUntilCollideMetapart(filteredSelectionMetapart, filteredSelectionMetapart.Children, unitDiff, totalDist) end else invisiblePart.Parent = workspace if List.itemsHasItemNotInList(invisiblePart:GetTouchingParts(), filteredSelectionMetapart.Children) then local unitDiff = positionDiff.Unit invisiblePart.CFrame = previousPosition moveUntilCollideWrapper(invisiblePart, 0.0002, filteredSelectionMetapart.Children, unitDiff, totalDist) end invisiblePart.Parent = nil filteredSelectionMetapart.TranslateFromTo(previousPosition, invisiblePart.CFrame) end else filteredSelectionMetapart.TranslateFromTo(previousPosition, invisiblePart.CFrame) end elseif currentHandle == R_XY then local halfSize = originalSize * 0.5 local handleSide = originalCFrame:pointToObjectSpace(initialPos) handleSide = handleSide.Z / math.abs(handleSide.Z) local radius = math.max(originalSize.X, originalSize.Y) local isInside = (originalCFrame:toWorldSpace(CFrame.new(Vector3.new(0,0,halfSize.Z * handleSide))).p - intersectionPoint).magnitude < radius local localPosition = originalCFrame:pointToObjectSpace(originalPosition) local newPosition = localPosition + currentDist local change = math.atan2(newPosition.Y, newPosition.X) - math.atan2(localPosition.Y, localPosition.X) local nearestChange = Round.roundToNearest(math.deg(change), isInside and 22.5 or 1) local expected = originalCFrame * CFrame.Angles(0, 0, math.rad(nearestChange)) if filteredSelectionMetapart:IsA("BasePart") then filteredSelectionMetapart.CFrame = expected elseif filteredSelectionMetapart:IsA("Model") and not filteredSelectionMetapart:IsA("Workspace") then local previousCFrame = filteredSelectionMetapart:GetModelCFrame() setModelCFrame(filteredSelectionMetapart, expected) if plugin.CollisionEnabled and #filteredSelectionMetapart:GetTouchingParts() > 0 then setModelCFrame(filteredSelectionMetapart, previousCFrame) end elseif filteredSelectionMetapart:IsA("Grouping") then local previousCFrame = filteredSelectionMetapart.CFrame filteredSelectionMetapart.CFrame = expected if plugin.CollisionEnabled and #filteredSelectionMetapart:GetTouchingParts() > 0 then filteredSelectionMetapart.CFrame = previousCFrame end end if filteredSelectionMetapart and (not plugin.CollisionEnabled or not List.itemsHasItemNotInList(filteredSelectionMetapart:GetTouchingParts(), selection)) then local planeNormal = (originalCFrame:pointToWorldSpace(Vector3.new(0, 0, 1)) - originalCFrame.p).Unit local initialAngle = (originalCFrame:pointToObjectSpace(initialPos) * Vector3.new(1, 1, 0)).unit local zVector = Vector3.new(0, -1, 0) local out = cosineSimilarity(zVector, initialAngle) --* (initialAngle.X > 0 and 1 or -1) local rotationalOffset = out if (initialAngle.X > 0) then rotationalOffset = rotationalOffset + 90 else if (initialAngle.Y > 0) then rotationalOffset = rotationalOffset + 180 + 45 else rotationalOffset = rotationalOffset - 45 end end Adorn.showRotate(originalCFrame:toWorldSpace(CFrame.new(Vector3.new(0,0,halfSize.Z * handleSide)))* CFrame.Angles(math.rad(0), math.rad(0),math.rad(rotationalOffset) ), radius, nearestChange, intersectionPoint) end elseif currentHandle == R_XZ then local halfSize = originalSize * 0.5 local handleSide = originalCFrame:pointToObjectSpace(initialPos) handleSide = handleSide.Y / math.abs(handleSide.Y) local radius = math.max(originalSize.X, originalSize.Z) local isInside = (originalCFrame:toWorldSpace(CFrame.new(Vector3.new(0,halfSize.Y * handleSide,0))).p - intersectionPoint).magnitude < radius local localPosition = originalCFrame:pointToObjectSpace(originalPosition) local newPosition = localPosition + currentDist local change = math.atan2(newPosition.Z, newPosition.X) - math.atan2(localPosition.Z, localPosition.X) local nearestChange = Round.roundToNearest(math.deg(change), isInside and 22.5 or 1) local expected = originalCFrame * CFrame.Angles(0, -math.rad(nearestChange), 0) if filteredSelectionMetapart:IsA("BasePart") then filteredSelectionMetapart.CFrame = expected elseif filteredSelectionMetapart:IsA("Grouping") or (filteredSelectionMetapart:IsA("Model") and not filteredSelectionMetapart:IsA("Workspace")) then local previousCFrame = filteredSelectionMetapart.CFrame filteredSelectionMetapart.CFrame = expected if plugin.CollisionEnabled and #filteredSelectionMetapart:GetTouchingParts() > 0 then filteredSelectionMetapart.CFrame = previousCFrame end end if filteredSelectionMetapart and (not plugin.CollisionEnabled or not List.itemsHasItemNotInList(filteredSelectionMetapart:GetTouchingParts(), selection)) then local planeNormal = (originalCFrame:pointToWorldSpace(Vector3.new(currentHandle == R_YZ and 1 or 0, currentHandle == R_XZ and 1 or 0, currentHandle == R_XY and 1 or 0)) - originalCFrame.p).Unit local initialAngle = (originalCFrame:pointToObjectSpace(initialPos) * Vector3.new(1, 0, 1)).unit local zVector = Vector3.new(0, 0, -1) local out = cosineSimilarity(zVector, initialAngle) * (initialAngle.X > 0 and 1 or -1) local rotationalOffset = out + 90 Adorn.showRotate(originalCFrame:toWorldSpace(CFrame.new(Vector3.new(0,halfSize.Y * handleSide,0)))* CFrame.Angles(math.rad(90),0 ,math.rad(rotationalOffset) ), radius, nearestChange, intersectionPoint) end elseif currentHandle == R_YZ then local halfSize = originalSize * 0.5 local handleSide = originalCFrame:pointToObjectSpace(initialPos) handleSide = handleSide.X / math.abs(handleSide.X) local radius = math.max(originalSize.Y, originalSize.Z) local isInside = (originalCFrame:toWorldSpace(CFrame.new(Vector3.new(halfSize.X * handleSide,0,0))).p - intersectionPoint).magnitude < radius local localPosition = originalCFrame:pointToObjectSpace(originalPosition) local newPosition = localPosition + currentDist local change = math.atan2(newPosition.Z, newPosition.Y) - math.atan2(localPosition.Z, localPosition.Y) local nearestChange = Round.roundToNearest(math.deg(change), isInside and 22.5 or 1) local expected = originalCFrame * CFrame.Angles(math.rad(nearestChange), 0, 0) if filteredSelectionMetapart:IsA("BasePart") then filteredSelectionMetapart.CFrame = expected elseif filteredSelectionMetapart:IsA("Model") and not filteredSelectionMetapart:IsA("Workspace") then local previousCFrame = filteredSelectionMetapart:GetModelCFrame() setModelCFrame(filteredSelectionMetapart, expected) if plugin.CollisionEnabled and #filteredSelectionMetapart:GetTouchingParts() > 0 then setModelCFrame(filteredSelectionMetapart, previousCFrame) end elseif filteredSelectionMetapart:IsA("Grouping") then local previousCFrame = filteredSelectionMetapart.CFrame filteredSelectionMetapart.CFrame = expected if plugin.CollisionEnabled and #filteredSelectionMetapart:GetTouchingParts() > 0 then filteredSelectionMetapart.CFrame = previousCFrame end end --rotation adorn if filteredSelectionMetapart and (not plugin.CollisionEnabled or not List.itemsHasItemNotInList(filteredSelectionMetapart:GetTouchingParts(), selection)) then local planeNormal = (originalCFrame:pointToWorldSpace(Vector3.new(1, 0, 0)) - originalCFrame.p).Unit local initialAngle = (originalCFrame:pointToObjectSpace(initialPos) * Vector3.new(0, 1, 1)).unit local zVector = Vector3.new(0, 0, -1) local out = cosineSimilarity(zVector, initialAngle) * (initialAngle.Y > 0 and 1 or -1) local rotationalOffset = out + 180 Adorn.showRotate(originalCFrame:toWorldSpace(CFrame.new(Vector3.new(halfSize.X * handleSide,0,0)))* CFrame.Angles(0,math.rad(90) ,math.rad(rotationalOffset) ), radius, nearestChange, intersectionPoint) end elseif currentHandle == H_PLANE and not currentlyOverHandle then Adorn.setAllAdornVisibility(false) if not planeDragging then if not Adorn.isPlaneSelectingModeOn() then return end planeDragging = true if not planeObject then planeObject = Instance.new("Part", cg) planeObject.FormFactor = Enum.FormFactor.Custom planeObject.Size = Vector3.new(50, 50, 0.01) planeObject.Position = Vector3.new(0,0,0) planeObject.Transparency = 1 end if not holoBox then holoBox = Instance.new("BoxHandleAdornment", cg) holoBox.Visible = false holoBox.Adornee = planeObject holoBox.Size = holoBox.Adornee.Size holoBox.Transparency = 0.6 holoBox.Color3 = Color3.new(38.0 / 255.0, 136.0 / 255.0, 240.0 / 255.0) end end else allowAdornUpdate = false end if plugin.CollisionEnabled and #game:GetService("Selection"):Get() == 1 then if currentHandle == S_Y_POS or currentHandle == S_Z_POS or currentHandle == S_X_NEG or currentHandle == S_Z_NEG or currentHandle == S_X_POS_Z_POS or currentHandle == S_X_NEG_Z_POS or currentHandle == S_X_NEG_Z_NEG or currentHandle == S_X_POS_Z_NEG or currentHandle == R_XY or currentHandle == R_XZ or currentHandle == R_YZ then if selectedPart and List.itemsHasItemNotInList(selectedPart:GetTouchingParts(), filteredSelection) then if preactionCFrame then selectedPart.CFrame = preactionCFrame end if preActionSize then selectedPart.Size = preActionSize end end end end if allowAdornUpdate then updateDragPart() end if currentHandle ~= R_XY and currentHandle ~= R_XZ and currentHandle ~= R_YZ then updateInvisiblePart() end if currentHandle == T_Y_POS then updateRotatePart() end adornmentUpdateNeeded = true end function updateDragPart() if not dragPart then dragPart = Instance.new("Part", nil) dragPart.Name = "DragParte1b1aec5" dragPart.FormFactor = Enum.FormFactor.Custom dragPart.BottomSurface = Enum.SurfaceType.Smooth dragPart.TopSurface = Enum.SurfaceType.Smooth dragPart.Transparency = 1 if shouldBreakJoints then BreakJoints(dragPart) end end if not dragPartHoloBox then dragPartHoloBox = Instance.new("BoxHandleAdornment", cg) dragPartHoloBox.Visible = false dragPartHoloBox.Color3 = Color3.new(38.0 / 255.0, 136.0 / 255.0, 240.0 / 255.0) dragPartHoloBox.Transparency = .5 dragPartHoloBox.AlwaysOnTop = true end if dragPart then if #game:GetService("Selection"):Get() == 1 then local mainPart = Metapart.convertToPart(game:GetService("Selection"):Get()[1]) dragPart.Size = mainPart.Size dragPart.CFrame = mainPart.CFrame else dragPart.Size = invisiblePart.Size dragPart.CFrame = invisiblePart.CFrame end dragPart.Name = "DragParte1b1aec5" dragPart.Archivable = false dragPart.Parent = nil dragPart.FormFactor = Enum.FormFactor.Custom dragPart.BottomSurface = Enum.SurfaceType.Smooth dragPart.TopSurface = Enum.SurfaceType.Smooth if shouldBreakJoints then BreakJoints(dragPart) end end if dragPartHoloBox and selectedPart then dragPartHoloBox.Adornee = dragPart dragPartHoloBox.Size = dragPartHoloBox.Adornee.Size-- + Vector3.new(.01, .01, .01) dragPartHoloBox.AlwaysOnTop = true dragPartHoloBox.CFrame = dragPart.CFrame - dragPart.CFrame.p --dragPartHoloBox.Color3 = Color3.new(.2, .5, .1) --dragPartHoloBox.Visible = true --dragPartHoloBox.DrawFull = true --Remove for Demo - dragPartHoloBox.Visible = true end end function grabPart() --if not selectedPart then return end updateInvisiblePart() local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if not filteredSelectionMetapart then return end freeDragging = true originalSize = filteredSelectionMetapart.Size originalCFrame = filteredSelectionMetapart.CFrame previousAABBCFrame = filteredSelectionMetapart.PlaneAlignedCFrame local extent = getBestExtentRotation() if not extent then return end originalDragPartOrientation = extent + previousAABBCFrame.p updateDragPart() setPartRotation(dragPart, originalDragPartOrientation) Extent.setPartCFrameToExtents(dragPart, originalDragPartOrientation) end function getAllFaceNormals(part) local normals = {} table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(1,0,0)) - part.CFrame.p).Unit) table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(-1,0,0)) - part.CFrame.p).Unit) table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(0,1,0)) - part.CFrame.p).Unit) table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(0,-1,0)) - part.CFrame.p).Unit) table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(0,0,1)) - part.CFrame.p).Unit) table.insert(normals, (part.CFrame:pointToWorldSpace(Vector3.new(0,0,-1)) - part.CFrame.p).Unit) return normals end function getDirectedNormalsFromPart(part, vect) local faceNormals = getAllFaceNormals(part) local closestNormals = {} local planeNormal = invisiblePart.CFrame:pointToWorldSpace(Vector3.new(0,1,0)) - invisiblePart.Position local perpPlaneNormal = planeNormal:Cross(vect) / planeNormal:Cross(vect).Magnitude for i,v in ipairs(faceNormals) do local projection = projectVectorToPlane(v, planeNormal) projection = projectVectorToPlane(projection, perpPlaneNormal).Unit if FuzzyMath.fuzzyCompareVector3(projection, vect) then table.insert(closestNormals, v) end end return closestNormals --project all face normals to workplaneFrame --project all normals to plane perpendiculat to workplane with direction vect --check which normal units are equal to vect unir end function getClosestNormalFromPart(part, vect) local bestSim = nil local bestNormal = {} for i = 1, 3 do for j = 1, 2 do local normalLocal = Vector3.new((i == 1 and 1 or 0), (i == 2 and 1 or 0), (i == 3 and 1 or 0)) * (j == 1 and 1 or -1) local normalWorld = part.CFrame:pointToWorldSpace(normalLocal) - part.CFrame.p local sim = cosineSimilarity(normalWorld.Unit, vect) if bestSim == nil or sim < bestSim then bestSim = sim bestNormal = {normalWorld.Unit} elseif sim == bestSim then table.insert(bestNormal, normalWorld.Unit) end end end if bestSim ~= bestSim then for _,v in ipairs(bestNormal) do v = v * -1.0 end end return bestNormal end function getFirstPart(instances) local returnPart for i, v in ipairs(instances) do if v:IsA("BasePart") then return v end returnPart = getFirstPart(v:GetChildren()) if returnPart then return returnPart end end return nil end function getBestExtentRotation() --get primary part rotation local selection = game:GetService("Selection"):Get() if #selection == 0 then return CFrame.new() end for i, v in ipairs(selection) do if v:IsA("Model") and not v:IsA("Workspace") then if v.PrimaryPart then return v.PrimaryPart.CFrame end local primaryPart = getFirstPart(selection) if not primaryPart then return CFrame.new() end return primaryPart.CFrame elseif v:IsA("BasePart") then return v.CFrame end end --not parts to drag return nil end function getNormalOfFace(collidedPart, collidedLocation) if not collidedPart or not collidedLocation then return end local halfSize = collidedPart.Size / 2 local localCollisionLocation = collidedPart.CFrame:pointToObjectSpace(collidedLocation) local localNormal = Vector3.new(0,0,0) if FuzzyMath.fuzzyCompare(localCollisionLocation.X, halfSize.X) then --right localNormal = Vector3.new(1, 0, 0) elseif FuzzyMath.fuzzyCompare(localCollisionLocation.X, -halfSize.X) then --left localNormal = Vector3.new(-1, 0, 0) elseif FuzzyMath.fuzzyCompare(localCollisionLocation.Y, halfSize.Y) then --top localNormal = Vector3.new(0, 1, 0) elseif FuzzyMath.fuzzyCompare(localCollisionLocation.Y, -halfSize.Y) then --bottom localNormal = Vector3.new(0, -1, 0) elseif FuzzyMath.fuzzyCompare(localCollisionLocation.Z, halfSize.Z) then --back localNormal = Vector3.new(0, 0, 1) elseif FuzzyMath.fuzzyCompare(localCollisionLocation.Z, -halfSize.Z) then --front localNormal = Vector3.new(0, 0, -1) end return collidedPart.CFrame:pointToWorldSpace(localNormal) - collidedPart.CFrame.p end function getNormalFromClosestPoint(part, position) if not part or not position then return nil end local localPoint = part.CFrame:pointToObjectSpace(position) local halfSize = part.Size / 2 local absLocalPoint = Utility.absVector3(localPoint) local xz = halfSize.x / halfSize.z local pointXZ = absLocalPoint.x / absLocalPoint.z local direction = Vector3.new(0,0,0) if pointXZ > xz then local xy = halfSize.x / halfSize.y local pointXY = absLocalPoint.x / absLocalPoint.y if pointXY > xy then if localPoint.x < 0 then direction = Vector3.new(-1,0,0) else direction = Vector3.new(1,0,0) end else if localPoint.y < 0 then direction = Vector3.new(0,-1,0) else direction = Vector3.new(0,1,0) end end else local yz = halfSize.y / halfSize.z local pointYZ = absLocalPoint.y / absLocalPoint.z if pointYZ > yz then if localPoint.y < 0 then direction = Vector3.new(0,-1,0) else direction = Vector3.new(0,1,0) end else if localPoint.z < 0 then direction = Vector3.new(0,0,-1) else direction = Vector3.new(0,0,1) end end end return (part.CFrame:pointToWorldSpace(direction) - part.CFrame.p).Unit end function normalExistsInTable(t, normal) for i,v in pairs(t) do if v[2] == normal then return true end end return false end function getNormalOfCollidingFace(movingPos, collidingPos, planeNormal) local v = planeNormal * -1 local w = collidingPos - movingPos local c1 = w:Dot(v) local c2 = v:Dot(v) local b = c1 / c2 local Pb = movingPos + (b * v) return Pb end local sanitizationPrecision = 1000000 function sanitizeFloat(value) return value > 0.0 and (math.ceil((value * sanitizationPrecision) - 0.5) / sanitizationPrecision) or (math.floor((value * sanitizationPrecision) + 0.5) / sanitizationPrecision) end function sanitizeVector3(value) return Vector3.new(sanitizeFloat(value.x), sanitizeFloat(value.y), sanitizeFloat(value.z)) end function sanitizeCFrame(value) local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = value:components() return CFrame.new( sanitizeFloat(x), sanitizeFloat(y), sanitizeFloat(z), r00, r01, r02, r10, r11, r12, r20, r21, r22) end local firstMove = true local partMoved = false function movePart(delta, collision) Adorn.setAllAdornVisibility(false) if not secondaryPart then return end if not dragPart then return end partMoved = true adornmentUpdateNeeded = true local selection = getCurrentSelectionWithChildren() if not setAnchoredStateForMovingParts then setAnchoredStateForMovingParts = true for i, v in ipairs(selection) do states[i] = v.Anchored v.Anchored = true end end local extentRotation = getBestExtentRotation() if not extentRotation or not selectedPart then return end setPartRotation(dragPart, #selection > 1 and workplaneFrame or extentRotation) if firstMove then -- this is expensive, only doing first time firstMove = false Extent.setPartCFrameToExtents(dragPart, dragPart.CFrame) end local initialPos = dragPart.CFrame if not dragFromToolbox then local deltaLocal = invisiblePart.CFrame:pointToObjectSpace(delta + dragPart.Position) deltaLocal = deltaLocal * Vector3.new(1, 0, 1) delta = invisiblePart.CFrame:pointToWorldSpace(deltaLocal) delta = delta - invisiblePart.Position end local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() setPartPosition(dragPart, previousAABBCFrame.p + delta) if shouldBreakJoints then UnjoinSelection() end local objectCFrame = workplaneFrame:toObjectSpace(dragPart.CFrame) local verts = {} local halfSize = selectedPart.Size / 2 local minSnappingDist = 100 local closestVectorIndex = nil local dragHalf = dragPart.Size / 2 --Snap bounding box verts verts[0] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new(-1,-1,-1))) verts[1] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new(-1,-1, 1))) verts[2] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new(-1, 1,-1))) verts[3] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new(-1, 1, 1))) verts[4] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new( 1,-1,-1))) verts[5] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new( 1,-1, 1))) verts[6] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new( 1, 1,-1))) verts[7] = workplaneFrame:pointToObjectSpace(dragPart.CFrame:pointToWorldSpace(dragHalf * Vector3.new( 1, 1, 1))) local minXDist = 100 local minYDist = 100 local minZDist = 100 local minSnapMovementX = 0 local minSnapMovementY = 0 local minSnapMovementZ = 0 for i = 0, #verts do local xDist = verts[i].x - roundToNearestGrid(verts[i].x) local yDist = verts[i].y - roundToNearestGrid(verts[i].y) local zDist = verts[i].z - roundToNearestGrid(verts[i].z) if math.abs(xDist) < minXDist then minXDist = math.abs(xDist) minSnapMovementX = xDist end if math.abs(yDist) < minYDist then minYDist = math.abs(yDist) minSnapMovementY = yDist end if math.abs(zDist) < minZDist then minZDist = math.abs(zDist) minSnapMovementZ = zDist end end local snappedDelta = Vector3.new(minSnapMovementX, minSnapMovementY, minSnapMovementZ) snappedDelta = snappedDelta * Vector3.new(1, 0, 1) snappedDelta = sanitizeVector3(snappedDelta) objectCFrame = objectCFrame - snappedDelta objectCFrame = sanitizeCFrame(objectCFrame) if collision and not dragFromToolbox then local planeNormal = invisiblePart.CFrame:pointToWorldSpace(Vector3.new(0,1,0)) - invisiblePart.Position setPartPosition(dragPart, workplaneFrame:toWorldSpace(objectCFrame).p) --setPartRotation(dragPart, invisiblePart.CFrame) if not originalDragPartOrientation then return end setPartRotation(dragPart, #selection > 1 and workplaneFrame or originalDragPartOrientation)--originalDragPartOrientation) USE THIS FOR LOCAL SPACE DRAG local preSafeMove = dragPart.CFrame --TODO, switch to this table.insert(selection, dragPart) dragPart.Parent = workspace dragPart.CFrame = initialPos dragPart.CFrame = preSafeMove if List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) then local dragVector = (dragPart.CFrame.p - initialPos.p).Unit if dragVector.x ~= dragVector.x then dragVector = Vector3.new(0,0,0) end local collidedPart, collidedLocation local partNormalTable = {} safeMoveWrapper(dragPart, 0.0002, selection, dragVector * -1) if Utility.distanceVector3(initialPos.p, preSafeMove.p) <= Utility.distanceVector3(dragPart.CFrame.p, preSafeMove.p) then dragPart.CFrame = initialPos end local afterInitialSafeMovePos = dragPart.CFrame dragPart.CFrame = dragPart.CFrame + (dragVector * .0001) --move back in slightly (for collision purposes local i = 0 while not List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) and i < 100 do dragPart.CFrame = dragPart.CFrame + (dragVector * .0001) i = i + 1 end local collideFrame = dragPart.CFrame local collidingParts = List.filterOutItems(selection, dragPart:GetTouchingParts()) --consolidate normals local collidingPartsCFrameList = {} --Iterate over colliding parts to consolidate colliding normals local beginTime = tick() for _,v in pairs(collidingParts) do --checked = checked + 1 local rotation = v.CFrame - v.CFrame.p --if not existsInTable(collidingPartsCFrameList, rotation) then --innerLoop = innerLoop + 1 table.insert(collidingPartsCFrameList, rotation) local Pb = getNormalOfCollidingFace(dragPart.CFrame.p, v.CFrame.p, planeNormal) local faceNormal = getNormalFromClosestPoint(v, Pb).Unit --constraining to plane faceNormal = faceNormal - (faceNormal:Dot(planeNormal.Unit) * planeNormal.Unit) --round please faceNormal = Vector3.new(FuzzyMath.fuzzyCompare(0,faceNormal.x) and 0 or faceNormal.x, FuzzyMath.fuzzyCompare(0,faceNormal.y) and 0 or faceNormal.y, FuzzyMath.fuzzyCompare(0,faceNormal.z) and 0 or faceNormal.z) faceNormal = Round.roundVector3ToNearest(faceNormal, 0.0001) if not normalExistsInTable(partNormalTable, faceNormal) then table.insert(partNormalTable, {v, faceNormal}) end end local endTime = tick() --for each part safe move with white list out by normal(even if not colliding) local finalPositionTable = {} local localFaceNormalTable = {} local tick1 --= tick() local tick2 --= tick() local tick3 --= tick() for _,v in pairs(partNormalTable) do dragPart.CFrame = initialPos local lowerBounds, upperBounds = Extent.getPartBounds(dragPart, v[1].CFrame) lowerBounds = v[1].CFrame:pointToObjectSpace(lowerBounds) upperBounds = v[1].CFrame:pointToObjectSpace(upperBounds) dragPart.CFrame = preSafeMove local lowerBounds2, upperBounds2 = Extent.getPartBounds(dragPart, v[1].CFrame) lowerBounds2 = v[1].CFrame:pointToObjectSpace(lowerBounds2) upperBounds2 = v[1].CFrame:pointToObjectSpace(upperBounds2) lowerBounds, upperBounds = Extent.unionVector3NoSpaceChange(lowerBounds2, lowerBounds, upperBounds) lowerBounds, upperBounds = Extent.unionVector3NoSpaceChange(upperBounds2, lowerBounds, upperBounds) local p1 = afterInitialSafeMovePos.p setPartPosition(dragPart, v[1].CFrame.p) safeMoveWhiteList(dragPart, v[2], {v[1]}) --dragPart:SafeMoveWhiteList(v[2], {v[1]}) local p2 = dragPart.CFrame.p p1 = Round.roundVector3ToNearest(p1, 0.0001) p2 = Round.roundVector3ToNearest(p2, 0.0001) local p3 = (p1 - p2).Unit if Utility.distanceVector3(preSafeMove.p, afterInitialSafeMovePos.p - p3) < Utility.distanceVector3(preSafeMove.p, afterInitialSafeMovePos.p + p3) then p3 = p3 * -1 end --project to plane p3 = p3 - (p3:Dot(planeNormal.Unit) * planeNormal.Unit) p3 = getClosestNormalFromPart(v[1], p3) if #p3 > 0 then p3 = p3[1] else p3 = nil end --zero out p3 = Vector3.new(FuzzyMath.fuzzyCompare(0,p3.x) and 0 or p3.x, FuzzyMath.fuzzyCompare(0,p3.y) and 0 or p3.y, FuzzyMath.fuzzyCompare(0,p3.z) and 0 or p3.z) setPartPosition(dragPart, afterInitialSafeMovePos.p - v[2]) safeMoveWhiteList(dragPart, p3, {v[1]}) --dragPart:SafeMoveWhiteList(p3, {v[1]}) local distToTest = Utility.distanceVector3(dragPart.CFrame.p, preSafeMove) dragPart.CFrame = preSafeMove safeMoveWhiteList(dragPart, v[2], {v[1]}) local secondDist = Utility.distanceVector3(dragPart.CFrame.p, afterInitialSafeMovePos) if secondDist < distToTest then distToTest = secondDist end dragPart.CFrame = afterInitialSafeMovePos local collSel = game.Selection:Get() table.insert(collSel, v[1]) if not moveUntilCollideWrapper(dragPart, 0.0002, collSel, p3, distToTest) then local currentLocation = dragPart.CFrame.p dragPart.CFrame = preSafeMove local moveVector = (dragPart.CFrame.p - currentLocation).Unit safeMoveWrapper(dragPart, 0.123, selection, v[2]) else local distTraveled = Utility.distanceVector3(dragPart.CFrame.p, afterInitialSafeMovePos.p) end local firstPosition = nil local localPosition = v[1].CFrame:pointToObjectSpace(dragPart.CFrame.p) local loE = vector3LessThanOrEqualTo(localPosition, upperBounds) local goE = vector3GreaterThanOrEqualTo(localPosition, lowerBounds) --dont remember what loE was for --loE = true if loE and goE then if not List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) then table.insert(finalPositionTable, dragPart.CFrame.p) else firstPosition = dragPart.CFrame.p end end dragPart.CFrame = preSafeMove --dragPart:SafeMove(v[2], selection) safeMoveWrapper(dragPart, 0.0002, selection, v[2]) localPosition = v[1].CFrame:pointToObjectSpace(dragPart.CFrame.p) if vector3LessThanOrEqualTo(localPosition, upperBounds) and vector3GreaterThanOrEqualTo(localPosition, lowerBounds) then if not List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) then table.insert(finalPositionTable, dragPart.CFrame.p) end end --try moving out by the closest movingPart face normal --getClosest face normal local normals = getDirectedNormalsFromPart(dragPart, v[2]) if firstPosition then for _,vNorm in pairs(normals) do if not List.itemExistsInList(vNorm, localFaceNormalTable) then table.insert(localFaceNormalTable, vNorm) setPartPosition(dragPart, firstPosition) safeMoveWrapper(dragPart, 0.0002, selection, vNorm) localPosition = v[1].CFrame:pointToObjectSpace(dragPart.CFrame.p) if not List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) then table.insert(finalPositionTable, dragPart.CFrame.p) end end end end end local tick4 = tick() local closestPos = initialPos.p local closestDist = Utility.distanceVector3(closestPos, preSafeMove.p) for _,v in pairs(finalPositionTable) do local testingDist = Utility.distanceVector3(v, preSafeMove.p) if testingDist < closestDist then closestDist = testingDist closestPos = v end end setPartPosition(dragPart, closestPos) --if dragPart:IsColliding(0.0001, selection) then -- setPartPosition(dragPart, afterInitialSafeMovePos.p) --end ----]]COLLISION CORRECT END end if Utility.distanceVector3(dragPart.CFrame.p, preSafeMove.p) > Utility.distanceVector3(initialPos.p, preSafeMove.p) then setPartPosition(dragPart, initialPos.p) end --local selection = getCurrentSelectionWithChildren() if List.itemsHasItemNotInList(dragPart:GetTouchingParts(), selection) then local coll = List.filterOutItems(selection, dragPart:GetTouchingParts()) dragPart.CFrame = initialPos end dragPart.Parent = nil local castOffset = 0.1 local threshHold = 20 + castOffset else local newPosition = workplaneFrame:pointToWorldSpace(objectCFrame.p) newPosition = sanitizeVector3(newPosition) dragPart.CFrame = dragPart.CFrame - dragPart.CFrame.p + newPosition if dragFromToolbox then local ini1 = dragPart.CFrame.p dragPart.Parent = game.Workspace safeMoveWrapper(dragPart, 0.0002, game.Selection:Get(), Vector3.new(0, 1, 0)) dragPart.Parent = nil local ini2 = dragPart.CFrame.p dragFromToolbox = false end end filteredSelectionMetapart.TranslateFromTo(initialPos, dragPart.CFrame) end function planeDrag() Adorn.setAllAdornVisibility(false) local ray = mouse.UnitRay ray = Ray.new(ray.Origin, ray.Direction * 800) local part, tmpLocation = game.Workspace:FindPartOnRay(ray) if not part or part:IsA("Terrain") then return end holoBox.Visible = true local location = part and rayBoxIntersection(ray, part.CFrame, part.Size) if part and location then local localLocation = part.CFrame:pointToObjectSpace(location) local v0, v1, v2 = nil local halfSize = part.Size /2 local planeSize = Vector3.new(50, 50, 0.01) local centerPoint = location planeObject.CFrame = part.CFrame planeObject.CFrame = planeObject.CFrame * CFrame.Angles(0, math.rad(90), 0) if FuzzyMath.fuzzyCompare(math.abs(localLocation.X), halfSize.X) then v0 = localLocation + Vector3.new(0,0,1) v1 = localLocation + Vector3.new(0,1,1) v2 = localLocation + Vector3.new(0,1,0) if FuzzyMath.fuzzyCompare(localLocation.X, halfSize.X) then planeSize = Vector3.new(part.Size.z, part.Size.y, 0.01) centerPoint = Vector3.new(halfSize.x, 0, 0) else planeSize = Vector3.new(part.Size.z, part.Size.y, 0.01) centerPoint = Vector3.new(-halfSize.x, 0, 0) end elseif FuzzyMath.fuzzyCompare(math.abs(localLocation.Y), halfSize.Y) then v0 = localLocation + Vector3.new(0,0,1) v1 = localLocation + Vector3.new(1,0,1) v2 = localLocation + Vector3.new(1,0,0) planeObject.CFrame = planeObject.CFrame * CFrame.Angles(math.rad(90), 0, 0) if FuzzyMath.fuzzyCompare(localLocation.Y, halfSize.Y) then planeSize = Vector3.new(part.Size.z, part.Size.x, 0.01) centerPoint = Vector3.new(0, halfSize.y, 0) else planeSize = Vector3.new(part.Size.z, part.Size.x, 0.01) centerPoint = Vector3.new(0, -halfSize.y, 0) end elseif FuzzyMath.fuzzyCompare(math.abs(localLocation.Z), halfSize.Z) then v0 = localLocation + Vector3.new(0,1,0) v1 = localLocation + Vector3.new(1,1,0) v2 = localLocation + Vector3.new(1,0,0) planeObject.CFrame = planeObject.CFrame * CFrame.Angles(0, math.rad(90), 0) if FuzzyMath.fuzzyCompare(localLocation.Z, halfSize.Z) then planeSize = Vector3.new(part.Size.x, part.Size.y, 0.01) centerPoint = Vector3.new(0, 0, halfSize.z) else planeSize = Vector3.new(part.Size.x, part.Size.y, 0.01) centerPoint = Vector3.new(0, 0, -halfSize.z) end end --[[ CSG Changes planeSize = Vector3.new(5, 5, 0.2) centerPoint = location planeObject.CFrame = CFrame.new(Vector3.new(0,0,0), castNormal) --]] if v0 and v1 and v2 then local partDist = part.CFrame:pointToObjectSpace(location) - localLocation local v0local = v0 + partDist local v1local = v1 + partDist local v2local = v2 + partDist v0 = part.CFrame:pointToWorldSpace(v0) v1 = part.CFrame:pointToWorldSpace(v1) v2 = part.CFrame:pointToWorldSpace(v2) v0local = part.CFrame:pointToWorldSpace(v0local) v1local = part.CFrame:pointToWorldSpace(v1local) v2local = part.CFrame:pointToWorldSpace(v2local) baseDragPlane = createPlane(v0, v1, v2) dragPlane = createPlane(v0local, v1local, v2local) --[[ CSG Changes baseDragPlane.normal = castNormal dragPlane.normal = castNormal --]] centerPoint = part.CFrame:pointToWorldSpace(centerPoint)--remove with CSG Changes planeObject.CFrame = planeObject.CFrame - planeObject.CFrame.p + centerPoint planeObject.Size = planeSize holoBox.Size = planeSize end local halfSize = part.Size * 0.5 local corner = localLocation / Utility.absVector3(localLocation) local centerFrame = part.CFrame - part.CFrame.p + (part.CFrame:pointToWorldSpace(halfSize * corner)) --localLocation Adorn.drawPlaneCenter(centerFrame) --[[CSG Changes planeObject.CFrame = planeObject.CFrame - planeObject.CFrame.p + location --]] end end function freeDrag() if clickOnUpdate then selectPart() clickOnUpdate = false return end if planeDragging then planeDrag() return end if not freeDragging then RubberBand.updateRubberBand(Vector2.new(mouse.X, mouse.Y)) return end castPlane = nil local currentRay = mouse.UnitRay currentRay = Ray.new(currentRay.Origin, currentRay.Direction * 800) if not dragPlane then return end local colPoint = rayPlaneIntersection(currentRay, dragPlane) if not dragFromToolbox then local pTest = (colPoint - currentRay.Origin) pTest = pTest / currentRay.Unit.Direction if pTest.X < 0 or pTest.Y < 0 or pTest.Z < 0 then return end end if dragFromToolbox then colPoint = rayPlaneIntersection(currentRay, baseDragPlane) local colPart, colLocation = workspace:FindPartOnRayWithIgnoreList(currentRay, game.Selection:Get()) local cameraPos = workspace.CurrentCamera.CoordinateFrame.p local partDist = Utility.distanceVector3(colLocation, cameraPos) local planeDist = Utility.distanceVector3(colPoint, cameraPos) if FuzzyMath.fuzzyCompareVector3(currentRay.Direction.Unit, (cameraPos - colPoint).Unit) then planeDist = 800 end if planeDist < 2 then planeDist = 800 end if planeDist > 500 then if partDist > 500 then colPoint = cameraPos + (currentRay.Direction.Unit *30) end end end local previousPoint = Vector3.new(0,0,0) if selectedPart then previousPoint = selectedPart.Position else local filteredSelection= Selection.getFilteredSelection() if not filteredSelection or #filteredSelection == 0 then return end selectedPart = Metapart.convertToPart(filteredSelection[1]) end if not startLocation then return end movePart(colPoint - startLocation, plugin.CollisionEnabled) lastDist = lastDist + (selectedPart.Position - previousPoint) updateInvisiblePart() end function selectDragPlane(selectBase) local ray = nil --if we are over plane, return early if Adorn.isOverPlaneSelect() then Adorn.setPlaneSelectingMode(false) return end ray = mouse.UnitRay ray = Ray.new(ray.Origin, ray.Direction* 800) --TODO: add tmpNormal local tmpPart, tmpNormal, tmpLocation if selectBase then tmpPart = Instance.new("Part", cg) tmpPart.FormFactor = Enum.FormFactor.Custom tmpPart.Size = Vector3.new(100, 1, 100) tmpPart.CFrame = CFrame.new(0, -0.5, 0) tmpLocation = Vector3.new(0,0,0) tmpNormal = Vector3.new(0, 1, 0) else tmpPart = game.Workspace:FindPartOnRay(ray) end if not tmpPart then Adorn.setPlaneSelectingMode(false) return end if not selectBase then tmpLocation = rayBoxIntersection(ray, tmpPart.CFrame, tmpPart.Size) end if secondaryPart then secondaryPart:Destroy() end secondaryPart = tmpPart secondaryLocation = tmpLocation --use proxy part local tmpPart = Instance.new("Part", cg) tmpPart.FormFactor = Enum.FormFactor.Custom tmpPart.Size = secondaryPart.Size tmpPart.CFrame = secondaryPart.CFrame secondaryPart = tmpPart secondaryPartCFrame = secondaryPart.CFrame:toWorldSpace(CFrame.new(secondaryPart.Size / 2)) secondaryPartSideSelected = nil local localLocation = nil local halfSize = nil if secondaryPart and secondaryLocation then localLocation = secondaryPart.CFrame:pointToObjectSpace(secondaryLocation) local v0, v1, v2 = nil -- below aren't locals, and they're not meant to be w0, w1, w2, w3 = nil -- global variables halfSize = secondaryPart.Size /2 if FuzzyMath.fuzzyCompare(math.abs(localLocation.X), halfSize.X) then v0 = localLocation + Vector3.new(0,0,1) v1 = localLocation + Vector3.new(0,1,1) v2 = localLocation + Vector3.new(0,1,0) w0 = Vector3.new(localLocation.X,0,0) + Vector3.new(0,-1,1) * halfSize w1 = Vector3.new(localLocation.X,0,0) + Vector3.new(0,1,1) * halfSize w2 = Vector3.new(localLocation.X,0,0) + Vector3.new(0,-1,-1) * halfSize w3 = Vector3.new(localLocation.X,0,0) + Vector3.new(0,1,-1) * halfSize --secondaryPart.CFrame = secondaryPart.CFrame * CFrame.Angles(0, 0, math.rad(90)) if FuzzyMath.fuzzyCompare(localLocation.X, halfSize.X) then secondaryPartSideSelected = RIGHT_SIDE else secondaryPartSideSelected = LEFT_SIDE end elseif FuzzyMath.fuzzyCompare(math.abs(localLocation.Y), halfSize.Y) then v0 = localLocation + Vector3.new(0,0,1) v1 = localLocation + Vector3.new(1,0,1) v2 = localLocation + Vector3.new(1,0,0) w0 = Vector3.new(0,localLocation.Y,0) + Vector3.new(-1,0,1) * halfSize w1 = Vector3.new(0,localLocation.Y,0) + Vector3.new(1,0,1) * halfSize w2 = Vector3.new(0,localLocation.Y,0) + Vector3.new(-1,0,-1) * halfSize w3 = Vector3.new(0,localLocation.Y,0) + Vector3.new(1,0,-1) * halfSize if FuzzyMath.fuzzyCompare(localLocation.Y, halfSize.Y) then secondaryPartSideSelected = TOP_SIDE else secondaryPartSideSelected = BOTTOM_SIDE end elseif FuzzyMath.fuzzyCompare(math.abs(localLocation.Z), halfSize.Z) then v0 = localLocation + Vector3.new(0,1,0) v1 = localLocation + Vector3.new(1,1,0) v2 = localLocation + Vector3.new(1,0,0) w0 = Vector3.new(0,0,localLocation.Z) + Vector3.new(-1,1,0) * halfSize w1 = Vector3.new(0,0,localLocation.Z) + Vector3.new(1,1,0) * halfSize w2 = Vector3.new(0,0,localLocation.Z) + Vector3.new(-1,-1,0) * halfSize w3 = Vector3.new(0,0,localLocation.Z) + Vector3.new(1,-1,0) * halfSize --secondaryPart.CFrame = secondaryPart.CFrame * CFrame.Angles(math.rad(90), 0, 0) if FuzzyMath.fuzzyCompare(localLocation.Z, halfSize.Z) then secondaryPartSideSelected = BACK_SIDE else secondaryPartSideSelected = FRONT_SIDE end end if v0 and v1 and v2 then pV0 = v0 pV1 = v1 pV2 = v2 v0 = secondaryPart.CFrame:pointToWorldSpace(v0) v1 = secondaryPart.CFrame:pointToWorldSpace(v1) v2 = secondaryPart.CFrame:pointToWorldSpace(v2) baseDragPlane = createPlane(v0, v1, v2) dragPlane = nil end end local pointLocation = localLocation / Utility.absVector3(localLocation) * -1 pointLocation = Utility.cleanVector3(pointLocation) workplaneFrame = secondaryPart.CFrame - (secondaryPart.CFrame:pointToWorldSpace(halfSize * pointLocation) - secondaryPart.CFrame.p) if secondaryPartSideSelected == LEFT_SIDE or secondaryPartSideSelected == RIGHT_SIDE then workplaneFrame = workplaneFrame * CFrame.Angles(0, 0, math.rad(90)) elseif secondaryPartSideSelected == BACK_SIDE or secondaryPartSideSelected == FRONT_SIDE then workplaneFrame = workplaneFrame * CFrame.Angles(math.rad(90), 0, 0) end updateInvisiblePart() --workplaneFrame = invisiblePart.CFrame - invisiblePart.CFrame.p + workplaneFrame.p local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if filteredSelectionMetapart then filteredSelectionMetapart.UpdatePlaneCFrame = workplaneFrame end Adorn.setPlaneSelectingMode(false) end function removeDragPart() local selection = {} local dragPartFound = false local currentSelection = game:GetService("Selection"):Get() for i, v in ipairs(currentSelection) do if v.Parent ~= cg and v.Name ~= "DragParte1b1aec5" and v.Name ~= "InvisibleParte1b1aec5" then table.insert(selection, v) else v.Parent = game:GetService("CoreGui") dragPartFound = true end end if dragPartFound then setSelection(selection) end end function releasePart() --local initialTick = tick() firstMove = true rotationalDiff = 0 if setAnchoredStateForMovingParts then -- while running local selection = getCurrentSelectionWithChildren() setAnchoredStateForMovingParts = false for i, v in ipairs(selection) do v.Anchored = states[i] end states = {} end RubberBand.finishRubberbandDrag() recreateAdornment() -- add inside of finishRubberbandDrag? setWaypoint() if freeDragging then if #game.Selection:Get() == 1 and game.Selection:Get()[1]:IsA("BasePart") then Adorn.setAllAdornVisibility(true) end lastDist = Vector3.new(0,0,0) selectedPartCFrameBeforeDrag = nil if partMoved then JoinSelection() end elseif handleWasDragged then JoinSelection() end partMoved = false dragFromToolbox = false handleWasDragged = false freeDragging = false originalDragPartOrientation = nil if dragPart then dragPartHoloBox.Visible = false dragPart.Parent = nil dragPartHoloBox.Adornee = dragPart dragPartHoloBox.Size = dragPartHoloBox.Adornee.Size-- + Vector3.new(.01, .01, .01) dragPartHoloBox.AlwaysOnTop = true dragPartHoloBox.CFrame = dragPart.CFrame - dragPart.CFrame.p --dragPartHoloBox.Visible = true end if planeDragging and Adorn.isPlaneSelectingModeOn() then selectDragPlane() planeDragging = false end if holoBox then holoBox:Destroy() holoBox = nil end if planeObject then planeObject:Destroy() planeObject = nil end startLocation = nil end function grabHandle(handle, position) if not position then return end originalPosition = position currentDist = Vector3.new(0,0,0) if handle ~= R_XY and handle ~= R_XZ and handle ~= R_YZ and handle ~= H_PLANE then updateDragPart() end local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if not filteredSelectionMetapart then return end originalCFrame = filteredSelectionMetapart.CFrame originalSize = filteredSelectionMetapart.Size previousAABBCFrame = invisiblePart.CFrame mouseOffsetOnGrabHandle = nil --will be set in preUpdatePart preUpdatePart() end function releaseHandle() itemToUpdate = nil rotationalDiff = 0 local handle = Adorn.getCurrentHandle() if (handle == H_NONE) then return end JoinSelection() originalPosition = nil currentDist = nil originalSize = nil originalCFrame = nil previousAABBCFrame = nil end function recreateAdornment() if not adornmentUpdateNeeded then return end adornmentUpdateNeeded = false if not invisiblePart then invisiblePart = Instance.new("Part", nil) invisiblePart.Name = "InvisibleParte1b1aec5" invisiblePart.FormFactor = Enum.FormFactor.Custom invisiblePart.TopSurface = Enum.SurfaceType.Smooth invisiblePart.BottomSurface = Enum.SurfaceType.Smooth invisiblePart.Archivable = false invisiblePart.Transparency = 1 end if not castPart then castPart = Instance.new("Part") castPart.FormFactor = Enum.FormFactor.Custom end Adorn.adornInstanceWithTranslate(invisiblePart) Adorn.adornInstanceWithPlane(invisiblePart) local selection = Selection.getFilteredSelection() if (#selection > 0) then Adorn.setPlaneVisibility(true) if (#selection == 1) and selection[1]:IsA("BasePart") then--and game.Selection:Get()[1]:IsA("BasePart") then Adorn.adornInstanceWithScale(selection[1]) else Adorn.setScaleAdornVisibility(false) end end updateRotatePart() updateInvisiblePart() end function isInSelection(part) if not part then return nil end local selection = game.Selection:Get() for _,p in ipairs(selection) do if p == part then return true end end return false end function getTopPVInstance(instance) if not instance then return nil end if (instance.Parent == game.Workspace) then if instance:IsA("PVInstance") and not instance:IsA("Workspace") then return instance end return nil end local returnInstance = getTopPVInstance(instance.Parent) if not returnInstance then if instance:IsA("PVInstance") and not instance:IsA("Workspace") then return instance end return nil end return returnInstance end function findPartInstance(instances) for _,v in ipairs(instances) do if v:IsA("BasePart") then return v end end for _,v in ipairs(instances) do local tmpPart = findPartInstance(v:GetChildren()) if tmpPart then return tmpPart end end return nil end function isAncestorSelected(part) if not part.Parent then return nil end if List.itemExistsInList(part.Parent, game.Selection:Get()) then return part.Parent end return isAncestorSelected(part.Parent) end function selectPart(instances) if currentlyOverHandle then return end castPlane = nil local ray = mouse.UnitRay ray = Ray.new(ray.Origin, ray.Direction * 800) local part, location = game.Workspace:FindPartOnRay(ray) local alreadySelected = false if List.itemExistsInList(part, game.Selection:Get()) then alreadySelected = true end if instances then local tmpPart = findPartInstance(instances) location = tmpPart.CFrame.p if tmpPart then part = tmpPart end if location.y < 0 then location = location * Vector3.new(1, 0, 1) end end if part and part.Locked and not dragFromToolbox then part = nil end if part and not uis:IsKeyDown(Enum.KeyCode.LeftAlt) and not alreadySelected then local ancestor = isAncestorSelected(part) if ancestor then part = ancestor else part = getTopPVInstance(part) end end if part then if not isInSelection(part) then if uis:IsKeyDown(Enum.KeyCode.LeftControl) then local selection = game.Selection:Get() table.insert(selection, part) setSelection(selection) else setSelection({part}) end elseif uis:IsKeyDown(Enum.KeyCode.LeftControl) then local selection = {} for _,p in ipairs(game.Selection:Get()) do if not(p == part) then table.insert(selection, p) end end setSelection(selection) end startLocation = location if pV0 and pV1 and pV2 then part = Metapart.convertToPart(part) local localLocation = secondaryPart.CFrame:pointToObjectSpace(secondaryLocation) local partDist = secondaryPart.CFrame:pointToObjectSpace(location) - localLocation local v0local = pV0 + partDist local v1local = pV1 + partDist local v2local = pV2 + partDist v0local = secondaryPart.CFrame:pointToWorldSpace(v0local) v1local = secondaryPart.CFrame:pointToWorldSpace(v1local) v2local = secondaryPart.CFrame:pointToWorldSpace(v2local) dragPlane = createPlane(v0local, v1local, v2local) local selectedPartRotation = part.CFrame - part.CFrame.p + startLocation local secondaryPartRotation = secondaryPart.CFrame - secondaryPart.CFrame.p local newRotation = CFrame.new(selectedPartRotation.p) * secondaryPartRotation if uis:IsKeyDown(Enum.KeyCode.LeftShift) then adornmentUpdateNeeded = true local partGroup = Selection.getFilteredSelectionMetapart() partGroup.CFrame = newRotation:toWorldSpace(selectedPartRotation:toObjectSpace(partGroup.CFrame)) end end grabPart() else RubberBand.startRubberbandDrag(Vector2.new(mouse.X, mouse.Y)) end if selectedPart and selectedPart:IsA("FormFactorPart") then selectedPart.FormFactor = Enum.FormFactor.Custom end recreateAdornment() end function waypointChanged() removeDragPart() if not on then return end resetDragger() --updateSelection() updateRotatePart(Selection.getFilteredSelection()) updateInvisiblePart() end function selectionChanged() adornmentUpdateNeeded = true Selection.updateSelection() resetDragger() if RubberBand.isRubberBandDragInProgress() then return end --dont update anything if still rubber band dragging local currentSelection = Selection.getCurrentSelection() local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if (#currentSelection == 0 and dragFromToolbox) then dragFromToolbox = false releasePart() end if dragPart then dragPart.Parent = nil end if filteredSelectionMetapart then originalSize = filteredSelectionMetapart.Size originalCFrame = filteredSelectionMetapart.CFrame end if (#currentSelection == 1) then selectedPart = Metapart.convertToPart(currentSelection[1]) if selectedPart and selectedPart:IsA("FormFactorPart") then selectedPart.FormFactor = Enum.FormFactor.Custom end end recreateAdornment() --updateInvisiblePart() for i,v in ipairs(currentSelection) do if v:IsA("BasePart") then if shouldBreakJoints then end elseif v:IsA("Model") and not v:IsA("Workspace") then Metapart.forcePrimaryPart(v) end end end function onDragEnter(instances) dragFromToolbox = true if not Input.getButtonState(Input.Enum.Key.MOUSE_BUTTON1) then Input.setButtonState(Input.Enum.Key.MOUSE_BUTTON1, true) selectPart(instances) end end local function togglePlaneSelection() if Adorn.isPlaneSelectingModeOn() then Adorn.setPlaneSelectingMode(false) planeDragging = false holoBox.Visible = false Adorn.clearExtraAdorns() else Adorn.setPlaneSelectingMode(true) planeDragging = true if not planeObject then planeObject = Instance.new("Part", cg) planeObject.FormFactor = Enum.FormFactor.Custom planeObject.Size = Vector3.new(50, 50, 0.01) planeObject.Position = Vector3.new(0,0,0) planeObject.Transparency = 1 end if not holoBox then holoBox = Instance.new("BoxHandleAdornment", cg) holoBox.Visible = false holoBox.Adornee = planeObject holoBox.Size = holoBox.Adornee.Size holoBox.Transparency = 0.6 holoBox.Color3 = Color3.new(38.0 / 255.0, 136.0 / 255.0, 240.0 / 255.0) end end end function keyPress(evt) if evt.UserInputType == Enum.UserInputType.Keyboard then if (evt.UserInputState == Enum.UserInputState.Begin) then if on and evt.KeyCode == Enum.KeyCode.Space then togglePlaneSelection() end if evt.KeyCode == Enum.KeyCode.Five and (uis:IsKeyDown(Enum.KeyCode.LeftControl) or uis:IsKeyDown(Enum.KeyCode.RightControl)) then if not on then On() else Off() end end end elseif on and evt.UserInputType == Enum.UserInputType.MouseButton1 then if (evt.UserInputState == Enum.UserInputState.Begin) then Input.setButtonState(Input.Enum.Key.MOUSE_BUTTON1, true) onPressMouse() elseif (evt.UserInputState == Enum.UserInputState.End) then Input.setButtonState(Input.Enum.Key.MOUSE_BUTTON1, Input.Enum.State.UP) onReleaseMouse() end end end function inputChanged(evt) if (Input.getButtonState(Input.Enum.Key.MOUSE_BUTTON1) and evt.UserInputType == Enum.UserInputType.MouseMovement) or (Adorn.isPlaneSelectingModeOn() and Adorn.getCurrentHandle() == H_PLANE) then if not originalCFrame then local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if filteredSelectionMetapart then originalCFrame = filteredSelectionMetapart.CFrame end end if not originalSize then local filteredSelectionMetapart = Selection.getFilteredSelectionMetapart() if filteredSelectionMetapart then originalSize = filteredSelectionMetapart.Size end end if originalCFrame then updatePart() end end freeDrag() end -------------------------------------------------- local updateAdornmentPositions = Adorn.updateAdornmentPositions function Off() if not on then return end on = false toolbarbutton:SetActive(false) waypointUndoConnection:disconnect() waypointRedoConnection:disconnect() --inputBeganConnection:disconnect() inputEndedConnection:disconnect() inputChangedConnection:disconnect() selectionChangedConnection:disconnect() renderSteppedConnection:disconnect() dragEnterConnection:disconnect() Adorn.destroyAdorns() end function On() if on then return end plugin:Activate(true) toolbarbutton:SetActive(true) on = true if rotateAdornPart then rotateAdornPart:Destroy() rotateAdornPart = nil end Adorn.initializeAdorns() waypointUndoConnection = game:GetService("ChangeHistoryService").OnUndo:connect(waypointChanged) waypointRedoConnection = game:GetService("ChangeHistoryService").OnRedo:connect(waypointChanged) inputEndedConnection = uis.InputEnded:connect(keyPress) inputChangedConnection = uis.InputChanged:connect(inputChanged) selectionChangedConnection = game:GetService("Selection").SelectionChanged:connect(selectionChanged) renderSteppedConnection = game:GetService("RunService").RenderStepped:connect(function() if Selection.getFilteredSelectionMetapart() then if Selection.getFilteredSelectionMetapart().IsUpdateRequired then spawn(function() updateAdornments() end) end end updateAdornmentPositions() end) dragEnterConnection = plugin:GetMouse().DragEnter:connect(onDragEnter) adornmentUpdateNeeded = true recreateAdornment() if not initializedDragPlane then selectDragPlane(true) end initializedDragPlane = true selectionChanged() end loaded = true inputBeganConnection = uis.InputBegan:connect(keyPress) ]]> Collision = -hSize.Y and xPlaneI.Z <= hSize.Z and xPlaneI.Z >= -hSize.Z) and (xPlaneI - oriOS).Magnitude or 10000 local yPlaneDist = (yPlaneI.X <= hSize.X and yPlaneI.X >= -hSize.X and yPlaneI.Z <= hSize.Z and yPlaneI.Z >= -hSize.Z) and (yPlaneI - oriOS).Magnitude or 10000 local zPlaneDist = (zPlaneI.X <= hSize.X and zPlaneI.X >= -hSize.X and zPlaneI.Y <= hSize.Y and zPlaneI.Y >= -hSize.Y) and (zPlaneI - oriOS).Magnitude or 10000 local minDist = Utility.min(xPlaneDist, yPlaneDist, zPlaneDist) if minDist == 10000 then return nil end if minDist == xPlaneDist then return part.CFrame:pointToWorldSpace(xPlaneI) elseif minDist == yPlaneDist then return part.CFrame:pointToWorldSpace(yPlaneI) else return part.CFrame:pointToWorldSpace(zPlaneI) end end local function castMove(part, threshold, ignoreList, direction) local collisions = List.filterOutItems(ignoreList, part:GetTouchingParts()) local castOutRay = Ray.new(part.CFrame.p, direction.Unit) local iPoint = nil local dist = 0 for i, v in ipairs(collisions) do local intersection = castOut(v, castOutRay) if intersection then local iDist = (intersection - part.CFrame.p).Magnitude if iDist > dist then dist = iDist iPoint = intersection end end end if iPoint then part.CFrame = part.CFrame - part.CFrame.p + iPoint end end local function safeMove(part, ignoreList, direction) if direction.magnitude == 0 then return end --collision safemove local originalCollision = List.filterOutItems(ignoreList, part:GetTouchingParts()) local threshold = 0.002 --if true then return end if #originalCollision > 0 and List.listDoesNotContainType(originalCollision, "Terrain") then --local initialTime = tick() local keepCasting = true while keepCasting do castMove(part, threshold, ignoreList, direction) local colliding = List.filterOutItems(originalCollision, List.filterOutItems(ignoreList, part:GetTouchingParts())) if #colliding == 0 then keepCasting = false else originalCollision = List.combineLists(originalCollision, colliding) end end searchOut(part, ignoreList, direction * -1, 0 - math.min(part.Size.X / 2, part.Size.Y / 2, part.Size.Z / 2), part.CFrame.p, nil) end end local function searchDirectionFine(part, ignoreList, movedSoFar, direction, moveBy, threshold, depth, isMetapart) if depth > 13 then return end if moveBy <= 0.0002 then return end movedSoFar = movePrimitivesDelta(part, direction.Unit * moveBy, movedSoFar, isMetapart) if movedSoFar ~= movedSoFar then return end if List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) then movedSoFar = movePrimitivesDelta(part, direction.Unit * -moveBy, movedSoFar, isMetapart) searchDirectionFine(part, ignoreList, movedSoFar, direction, moveBy * 0.5, threshold, depth + 1, isMetapart) else searchDirectionFine(part, ignoreList, movedSoFar, direction, moveBy, threshold, depth + 1, isMetapart) end end local function searchDirectionGross(part, ignoreList, movedSoFar, direction, threshold, isMetapart) if (movedSoFar.Magnitude > maxMove) then return end movedSoFar = movePrimitivesDelta(part, direction.Unit, movedSoFar, isMetapart) if (movedSoFar ~= movedSoFar) then return end if List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) then searchDirectionGross(part, ignoreList, movedSoFar, direction, threshold, isMetapart) else searchDirectionFine(part, ignoreList, movedSoFar, direction * -1.0, 0.5, threshold, 0, isMetapart) end end local function safeMoveAlongLine(part, ignoreList, direction, threshold, isMetapart) --Check if colliding? if List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) then searchDirectionGross(part, ignoreList, Vector3.new(0,0,0), direction * -1, threshold, isMetapart) end end local function searchDirectionUntilCollideGross(part, ignoreList, movedSoFar, direction, threshold, maxDist, isMetapart) local distanceTraveled = movedSoFar.Magnitude if (distanceTraveled > 1000) then return false end movedSoFar = movePrimitivesDelta(part, direction.Unit, movedSoFar, isMetapart) if (movedSoFar ~= movedSoFar) then return false end if List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) then safeMoveAlongLine(part, ignoreList, direction, threshold, isMetapart) return true else if (distanceTraveled + 1 > maxDist) then return false end return searchDirectionUntilCollideGross(part, ignoreList, movedSoFar, direction, threshold, maxDist, isMetapart) end end local function moveUntilCollide(part, ignoreList, direction, threshold, maxDist) if not List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) then return searchDirectionUntilCollideGross(part, ignoreList, Vector3.new(0,0,0), direction, threshold, maxDist) end return false end -----------------------Metapart-------------------------- local function moveUntilCollideMetapart(metapart, ignoreList, direction, maxDist) if not List.itemsHasItemNotInList(metapart:GetTouchingParts(), ignoreList) then return searchDirectionUntilCollideGross(metapart, ignoreList, Vector3.new(0,0,0), direction, 0.0002, maxDist, true) end return false end -------------------------------------------------------------- -------------------------------------------------------------- -------------------------------------------------------------- local module = {} module.SafeMove = safeMove module.MovePrimitivesDelta = movePrimitivesDelta module.moveUntilCollide = moveUntilCollide module.moveUntilCollideMetapart = moveUntilCollideMetapart return module ]]> Utility currentMax and v or currentMax end end return currentMax end function minVector3(...) local arg = {...} local currentMin = nil for i, v in ipairs(arg) do if not currentMin then currentMin = v elseif v then currentMin = Vector3.new(min(currentMin.x, v.x), min(currentMin.y, v.y), min(currentMin.z, v.z)) end end return currentMin end function maxVector3(...) local arg = {...} local currentMax = nil for i, v in ipairs(arg) do if not currentMax then currentMax = v elseif v then currentMax = Vector3.new (max(currentMax.x, v.x), max(currentMax.y, v.y), max(currentMax.z, v.z)) end end return currentMax end function absVector3(value) return Vector3.new(math.abs(value.x), math.abs(value.y), math.abs(value.z)) end function distanceVector3(v0, v1) return math.sqrt(math.pow(v1.x - v0.x, 2) + math.pow(v1.y - v0.y, 2) + math.pow(v1.z - v0.z, 2)) end function cleanVector3(value, replacement) if not replacement then replacement = 0 end return Vector3.new(value.X == value.X and value.X or replacement, value.Y == value.Y and value.Y or replacement, value.Z == value.Z and value.Z or replacement) end local function clamp(value, minimum, maximum) return math.max(math.min(value, maximum), minimum) end local function smoothstep(edge0, edge1, x) x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0) return x*x*(3 - 2*x) --return x*x*x*(x*(x*6 - 15) + 10) end local function colorMultiply(color, multiplier) return Color3.new(color.r * multiplier, color.g * multiplier, color.b * multiplier) end local function colorAdd(color1, color2) return Color3.new(color1.r + color2.r, color1.g + color2.g, color1.b + color2.b) end local function customToSymmetric(value) local v = value.X ~= 0 and value.X or (value.Y ~= 0 and value.Y or (value.Z ~= 0 and value.Z or 0)) return Vector3.new(v,v,v) end local function getVector3Sign(value) return Vector3.new(value.X / math.abs(value.X == 0 and 1 or value.X), value.Y / math.abs(value.Y == 0 and 1 or value.Y), value.Z / math.abs(value.Z == 0 and 1 or value.Z)) end -------------------------------------------------------------- -------------------------------------------------------------- -------------------------------------------------------------- local module = {} module.min = min module.max = max module.minVector3 = minVector3 module.maxVector3 = maxVector3 module.absVector3 = absVector3 module.distanceVector3 = distanceVector3 module.cleanVector3 = cleanVector3 module.getVector3Sign = getVector3Sign module.customToSymmetric = customToSymmetric module.smoothstep = smoothstep module.colorMultiply = colorMultiply module.colorAdd = colorAdd return module ]]> List Plane Selection Metapart 0.0 and (math.ceil((value * sanitizationPrecision) - 0.5) / sanitizationPrecision) or (math.floor((value * sanitizationPrecision) + 0.5) / sanitizationPrecision) end local function sanitizeCFrame(value, sanitizeRotation) local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = value:components() return CFrame.new( sanitizeFloat(x), sanitizeFloat(y), sanitizeFloat(z), sanitizeRotation and sanitizeFloat(r00) or r00, sanitizeRotation and sanitizeFloat(r01) or r01, sanitizeRotation and sanitizeFloat(r02) or r02, sanitizeRotation and sanitizeFloat(r10) or r10, sanitizeRotation and sanitizeFloat(r11) or r11, sanitizeRotation and sanitizeFloat(r12) or r12, sanitizeRotation and sanitizeFloat(r20) or r20, sanitizeRotation and sanitizeFloat(r21) or r21, sanitizeRotation and sanitizeFloat(r22) or r22) end function createMetaPart(object, subscribe) if not object then return nil end if (pcall(function() object:IsWrapped() end)) then return object end if not subscribe then subscribe = false end if type(object) == "table" and #object == 1 then object = object[1] end if type(object) == "table" then if #object == 0 then return nil end local finalObject = {} ---------------------------------------------------------- finalObject.objects = object finalObject.mt = {} finalObject.info = {} finalObject.info.oCFrame = nil --cframe - ObjectAligned finalObject.info.oSize = nil --size - ObjectAligned finalObject.info.pCFrame = nil --cframe - PlaneAligned finalObject.info.pSize = nil --size - PlaneAligned finalObject.info.lastPlaneCFrame = nil finalObject.children = getAllChildrenFromTable(finalObject.objects) finalObject.roots = getAllRootsFromTable(finalObject.objects) finalObject.info.expectingChanged = false finalObject.info.updateRequired = false local function subscribeToParts(parts) local subscriptionList = {} for i, v in ipairs(parts) do subscriptionList[i] = v.Changed:connect(function() if not finalObject.info.expectingChanged then finalObject.info.updateRequired = true end end) end return subscriptionList end if subscribe then finalObject.subscribe = subscribeToParts(finalObject.children) end ---------------------------------------------- setmetatable(finalObject, finalObject.mt) local function reRoot() finalObject.roots = getAllRootsFromTable(finalObject.objects) end local function recalculateCFrameAndSize() finalObject.info.oCFrame, finalObject.info.oSize = Extent.getCFrameAndSizeOfList(finalObject.children, createMetaPart(finalObject.objects[1]).CFrame) finalObject.info.oCFrame = sanitizeCFrame(finalObject.info.oCFrame) end local function getTouchingParts() local intersecting = {} local children = finalObject.children if (#children > collisionSizeLimit) then --check collision against cframe and size return {} end for i = 1, #children do local found = children[i]:GetTouchingParts() intersecting = List.combineLists(intersecting, List.filterOutItems(children, found)) end intersecting = List.removeDuplicates(intersecting) return intersecting end --Getters finalObject.mt.__index = function(t, key) if key == "IsWrapped" then return function() return true end end if key == "IsA" then return function(t, value) return value == "Grouping" or value == "Wrapped" end end if key == "Unsubscribe" then return function() if finalObject.subscribe then for i, v in ipairs(finalObject.subscribe) do v:disconnect() end end end end if key == "CanSimulate" then return #finalObject.children < collisionSizeLimit end if key == "IsUpdateRequired" then local updaterequired = finalObject.info.updateRequired finalObject.info.updateRequired = false return updaterequired end if key == "ClearCache" then return function() finalObject.info.oCFrame = nil finalObject.info.oSize = nil finalObject.info.pCFrame = nil finalObject.info.pSize = nil finalObject.info.lastPlaneCFrame = nil end end if key == "TranslateFromTo" then return function(from, to) finalObject.info.expectingChanged = true if not finalObject.info.oCFrame then recalculateCFrameAndSize() end reRoot() local difference = to.p - from.p--finalObject.info.oCFrame --sanitize? for i = 1, #finalObject.roots do finalObject.roots[i].CFrame = finalObject.roots[i].CFrame + difference end finalObject.info.pCFrame = finalObject.info.pCFrame + difference finalObject.info.oCFrame = sanitizeCFrame(finalObject.info.oCFrame + difference) finalObject.info.expectingChanged = false end end if key == "Children" then return finalObject.children end if key == "Size" then if finalObject.info.oSize then return finalObject.info.oSize end recalculateCFrameAndSize() return finalObject.info.oSize end if key == "CFrame" then if finalObject.info.oCFrame then return finalObject.info.oCFrame end recalculateCFrameAndSize() return finalObject.info.oCFrame end if key == "PlaneAlignedSize" then if finalObject.info.pSize then return finalObject.info.pSize end return nil end if key == "PlaneAlignedCFrame" then if finalObject.info.pCFrame then return finalObject.info.pCFrame end return nil end if key == "GetTouchingParts" then return function() return getTouchingParts() end end if key == "PlaneSet" then return finalObject.info.lastPlaneCFrame ~= nil end end --Setters finalObject.mt.__newindex = function(t, key, value) if key == "UpdatePlaneCFrame" then if finalObject.info.lastPlaneCFrame == value then return end finalObject.info.lastPlaneCFrame = value finalObject.info.pCFrame, finalObject.info.pSize = Extent.getCFrameAndSizeOfList(finalObject.objects, value) end if key == "CFrame" then finalObject.info.expectingChanged = true if not finalObject.info.oCFrame then recalculateCFrameAndSize() end reRoot() local rotationCoord = value - value.p for i = 1, #finalObject.roots do local primInCenter = finalObject.info.oCFrame:toObjectSpace(finalObject.roots[i].CFrame) local newPrimInCenter = rotationCoord * primInCenter finalObject.roots[i].CFrame = newPrimInCenter + finalObject.info.oCFrame.p end if (finalObject.info.oCFrame - finalObject.info.oCFrame.p) ~= (value - value.p) then finalObject.info.pCFrame = nil finalObject.info.pSize = nil finalObject.info.lastPlaneCFrame = nil elseif finalObject.info.pCFrame then finalObject.info.pCFrame = finalObject.info.pCFrame - finalObject.info.oCFrame.p + value.p end finalObject.info.oCFrame = sanitizeCFrame(value) finalObject.info.expectingChanged = false end end return finalObject elseif object:IsA("Model") and not object:IsA("Workspace") then if (pcall(function() object:IsWrapped() end)) then return object end forcePrimaryPart(object) local finalObject = {} ----------------------------------------- finalObject.object = object finalObject.mt = {} finalObject.info = {} finalObject.info.oCFrame = nil --cframe - ObjectAligned finalObject.info.oSize = nil --size - ObjectAligned finalObject.info.pCFrame = nil --cframe - PlaneAligned finalObject.info.pSize = nil --size - PlaneAligned finalObject.info.lastPlaneCFrame = nil finalObject.children = getAllChildrenFromTable({finalObject.object}) finalObject.roots = getAllRootsFromTable({finalObject.object}) finalObject.info.expectingChanged = false finalObject.info.updateRequired = false local function subscribeToParts(parts) local subscriptionList = {} for i, v in ipairs(parts) do subscriptionList[i] = v.Changed:connect(function() if not finalObject.info.expectingChanged then finalObject.info.updateRequired = true end end) end return subscriptionList end if subscribe then finalObject.subscribe = subscribeToParts(finalObject.children) end -------------------------------------------------- setmetatable(finalObject, finalObject.mt) local function reRoot() finalObject.roots = getAllRootsFromTable({finalObject.object}) end local function getCurrentCFrame() return finalObject.object:GetModelCFrame() end local function getTouchingParts() local children = getAllChildren(finalObject.object) local intersecting = {} for i = 1, #children do local found = children[i]:GetTouchingParts() intersecting = List.combineLists(intersecting, List.filterOutItems(children, found)) end intersecting = List.removeDuplicates(intersecting) return intersecting end finalObject.mt.__eq = function(a, b) return pcall(function() return createMetaPart(a).Object == createMetaPart(b).Object end) end local function recalculateCFrameAndSize() local extentFrame = nil if finalObject.object.PrimaryPart then extentFrame = finalObject.object.PrimaryPart.CFrame elseif #finalObject.children > 0 then extentFrame = finalObject.children[1].CFrame else extentFrame = CFrame.new() end finalObject.info.oCFrame, finalObject.info.oSize = Extent.getCFrameAndSizeOfList(finalObject.children, extentFrame) finalObject.info.oCFrame = sanitizeCFrame(finalObject.info.oCFrame) end finalObject.mt.__index = function (table, key) if key == "IsWrapped" then return function() return true end end if key == "IsA" then return function(t, value) return finalObject.object:IsA(value) or value == "Wrapped" end end if key == "Unsubscribe" then return function() if finalObject.subscribe then for i, v in ipairs(finalObject.subscribe) do v:disconnect() end end end end if key == "IsUpdateRequired" then local updaterequired = finalObject.info.updateRequired finalObject.info.updateRequired = false return updaterequired end if key == "CanSimulate" then return #finalObject.children < collisionSizeLimit end if key == "ClearCache" then return function() finalObject.info.oCFrame = nil finalObject.info.oSize = nil finalObject.info.pCFrame = nil finalObject.info.pSize = nil finalObject.info.lastPlaneCFrame = nil end end if key == "TranslateFromTo" then return function(from, to) finalObject.info.expectingChanged = true if not finalObject.info.oCFrame then recalculateCFrameAndSize() end reRoot() local difference = to.p - from.p--finalObject.info.oCFrame --sanitize? for i = 1, #finalObject.roots do finalObject.roots[i].CFrame = finalObject.roots[i].CFrame + difference end finalObject.info.pCFrame = finalObject.info.pCFrame + difference finalObject.info.oCFrame = finalObject.info.oCFrame + difference finalObject.info.expectingChanged = false end end if key == "BreakImplicitJoints" then return function() end end if key == "IsColliding" then return function() return false end end if key == "GetTouchingParts" then return function() return getTouchingParts() end end if key == "SafeRotate" then return function() end end if key == "Children" then return finalObject.children end if key == "Size" then if finalObject.info.oSize then return finalObject.info.oSize end recalculateCFrameAndSize() return finalObject.info.oSize end if key == "CFrame" then if finalObject.info.oCFrame then return finalObject.info.oCFrame end recalculateCFrameAndSize() return finalObject.info.oCFrame end if key == "PlaneAlignedSize" then if finalObject.info.pSize then return finalObject.info.pSize end return nil end if key == "PlaneAlignedCFrame" then if finalObject.info.pCFrame then return finalObject.info.pCFrame end return nil end if key == "Position" then if finalObject.info.oCFrame then return finalObject.info.oCFrame.p end recalculateCFrameAndSize() return finalObject.info.oCFrame.p end if key == "Object" then return finalObject.object end if (pcall(function() local test = finalObject.object[key] end)) then if type(finalObject.object[key]) == 'function' then return function(t, ...) return finalObject.object[key](finalObject.object, ...) end end return finalObject.object[key] end return nil end local function moveAllChildren(parentObject, oldCFrame, newCFrame) for _,v in ipairs(parentObject:GetChildren()) do moveAllChildren(v, oldCFrame, newCFrame) if v:IsA("BasePart") then v.CFrame = newCFrame:toWorldSpace(oldCFrame:toObjectSpace(v.CFrame)) end end end finalObject.mt.__newindex = function (table, key, value) if key == "UpdatePlaneCFrame" then finalObject.info.lastPlaneCFrame = value finalObject.info.pCFrame, finalObject.info.pSize = Extent.getCFrameAndSizeOfList(finalObject.children, value) return end if key == "CFrame" then finalObject.info.expectingChanged = true if not finalObject.info.oCFrame then recalculateCFrameAndSize() end reRoot() value = sanitizeCFrame(value, true) for i = 1, #finalObject.roots do finalObject.roots[i].CFrame = value * finalObject.info.oCFrame:toObjectSpace(finalObject.roots[i].CFrame) end if (finalObject.info.oCFrame - finalObject.info.oCFrame.p) ~= (value - value.p) then finalObject.info.pCFrame = nil finalObject.info.pSize = nil finalObject.info.lastPlaneCFrame = nil elseif finalObject.info.pCFrame then finalObject.info.pCFrame = finalObject.info.pCFrame - finalObject.info.oCFrame.p + value.p end finalObject.info.oCFrame = value finalObject.info.expectingChanged = false return end -------------------------------------------------------------------------------------- if key == "BreakImplicitJoints" then return function() end end if key == "Size" then return elseif key == "Position" then finalObject.info.expectingChanged = true local oldCFrame = getCurrentCFrame() local newCFrame = oldCFrame - oldCFrame.p + value moveAllChildren(finalObject.object, oldCFrame, newCFrame) finalObject.info.expectingChanged = false return end finalObject.info.expectingChanged = true if (pcall(function() local test = finalObject.object[key] end)) then finalObject.object[key] = value end finalObject.info.expectingChanged = false end return finalObject elseif object:IsA("BasePart") then if (pcall(function() object:IsWrapped() end)) then return object end local finalObject = {} ---------------------------------------- finalObject.object = object finalObject.mt = {} finalObject.info = {} finalObject.info.pCFrame = nil --cframe - PlaneAligned finalObject.info.pSize = nil --size - PlaneAligned finalObject.info.lastPlaneCFrame = nil finalObject.children = {object} finalObject.roots = getAllRootsFromTable({finalObject.object}) finalObject.info.expectingChanged = false finalObject.info.updateRequired = false local function subscribeToParts(parts) local subscriptionList = {} for i, v in ipairs(parts) do subscriptionList[i] = v.Changed:connect(function() if not finalObject.info.expectingChanged then finalObject.info.updateRequired = true end end) end return subscriptionList end if subscribe then finalObject.subscribe = subscribeToParts(finalObject.children) end --------------------------------------------------------------- local function reRoot() finalObject.roots = getAllRootsFromTable({finalObject.object}) end setmetatable(finalObject, finalObject.mt) local function getCurrentCFrame() return finalObject.object:GetModelCFrame() end finalObject.mt.__eq = function(a, b) return pcall(function() return createMetaPart(a).Object == createMetaPart(b).Object end) end finalObject.mt.__index = function (table, key) if key == "IsWrapped" then return true end if key == "IsA" then return function(t, value) return finalObject.object:IsA(value) or value == "Wrapped" end end if key == "TranslateFromTo" then return function(from, to) reRoot() finalObject.info.expectingChanged = true local difference = to.p - from.p --sanitize? for i = 1, #finalObject.roots do finalObject.roots[i].CFrame = finalObject.roots[i].CFrame + difference end finalObject.info.pCFrame = finalObject.info.pCFrame + difference finalObject.info.expectingChanged = false end end if key == "Unsubscribe" then return function() if finalObject.subscribe then for i, v in ipairs(finalObject.subscribe) do v:disconnect() end end end end if key == "CanSimulate" then return true end if key == "ClearCache" then return function() finalObject.info.oCFrame = nil finalObject.info.oSize = nil finalObject.info.pCFrame = nil finalObject.info.pSize = nil finalObject.info.lastPlaneCFrame = nil end end if key == "IsUpdateRequired" then local updaterequired = finalObject.info.updateRequired finalObject.info.updateRequired = false return updaterequired end if key == "Children" then return finalObject.children end if key == "CFrame" then return finalObject.object.CFrame elseif key == "Size" then return finalObject.object.Size elseif key == "Position" then return finalObject.object.CFrame.p elseif key == "Object" then return finalObject.object end if key == "PlaneAlignedSize" then if finalObject.info.pSize then return finalObject.info.pSize end return nil end if key == "PlaneAlignedCFrame" then if finalObject.info.pCFrame then return finalObject.info.pCFrame end return nil end if (pcall(function() local test = finalObject.object[key] end)) then if type(finalObject.object[key]) == 'function' then return function(t, ...) return finalObject.object[key](finalObject.object, ...) end end return finalObject.object[key] end return nil end finalObject.mt.__newindex = function (table, key, value) finalObject.info.expectingChanged = true if key == "UpdatePlaneCFrame" then finalObject.info.lastPlaneCFrame = value finalObject.info.pCFrame, finalObject.info.pSize = Extent.getCFrameAndSizeOfList({finalObject.object}, value) finalObject.info.expectingChanged = false return end if key == "CFrame" then value = sanitizeCFrame(value) finalObject.info.lastPlaneCFrame = nil end if key == "Size" then finalObject.info.lastPlaneCFrame = nil end if key == "Position" then finalObject.info.lastPlaneCFrame = nil end if (pcall(function() local test = finalObject.object[key] end)) then finalObject.object[key] = value end finalObject.info.expectingChanged = false end return finalObject else if (pcall(function() object:IsWrapped() end)) then return object end local finalObject = {} ---------------------------------------- finalObject.object = object finalObject.mt = {} finalObject.info = {} --------------------------------------------------------------- setmetatable(finalObject, finalObject.mt) finalObject.mt.__eq = function(a, b) return pcall(function() return createMetaPart(a).Object == createMetaPart(b).Object end) end finalObject.mt.__index = function (table, key) if key == "IsWrapped" then return true end if key == "IsA" then return function(t, value) return finalObject.object:IsA(value) or value == "Wrapped" end end if key == "CFrame" then return CFrame.new() elseif key == "Size" then return Vector3.new(0,0,0) elseif key == "Position" then return Vector3.new(0,0,0) elseif key == "Object" then return finalObject.object elseif key == "PlaneAlignedSize" then return Vector3.new(0,0,0) elseif key == "PlaneAlignedCFrame" then return CFrame.new() end if (pcall(function() local test = finalObject.object[key] end)) then if type(finalObject.object[key]) == 'function' then return function(t, ...) return finalObject.object[key](finalObject.object, ...) end end return finalObject.object[key] end return nil end finalObject.mt.__newindex = function (table, key, value) if key == "UpdatePlaneCFrame" then return end if key == "CFrame" then return end if key == "Size" then return end if key == "Position" then return end if (pcall(function() local test = finalObject.object[key] end)) then finalObject.object[key] = value end end return finalObject end return object end local module = {} module.convertToPart = createMetaPart module.forcePrimaryPart = forcePrimaryPart return module ]]> FuzzyMath = value1 - delta and value2 <= value1 + delta) then return true end return false end function fuzzyCompareVector3(value1, value2, delta) if fuzzyCompare(value1.x, value2.x, delta) and fuzzyCompare(value1.y, value2.y, delta) and fuzzyCompare(value1.z, value2.z, delta) then return true end return false end function fuzzyCompareCFrame(value1, value2, delta) local x1, y1, z1, r001, r011, r021, r101, r111, r121, r201, r211, r221 = value1:components() local x2, y2, z2, r002, r012, r022, r102, r112, r122, r202, r212, r222 = value2:components() if --fuzzyCompare(x1, x2, delta) and --fuzzyCompare(y1, y2, delta) and --fuzzyCompare(z1, z2, delta) and fuzzyCompare(r001, r002, delta) and fuzzyCompare(r011, r012, delta) and fuzzyCompare(r021, r022, delta) and fuzzyCompare(r101, r102, delta) and fuzzyCompare(r111, r112, delta) and fuzzyCompare(r121, r122, delta) and fuzzyCompare(r201, r202, delta) and fuzzyCompare(r211, r212, delta) and fuzzyCompare(r221, r222, delta) then return true end return false end function fuzzyRound(value) return Round.roundToNearest(value, 0.0001) end function visiblyIdentityCFrame(value, delta) local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = value:components() local r0 = false local r1 = false local r2 = false if fuzzyCompare(math.abs(r00), 1) then if r0 then return false end r0 = true elseif not fuzzyCompare(r00, 0) then return false end if fuzzyCompare(math.abs(r01), 1) then if r0 then return false end r0 = true elseif not fuzzyCompare(r01, 0) then return false end if fuzzyCompare(math.abs(r02), 1) then if r0 then return false end r0 = true elseif not fuzzyCompare(r02, 0) then return false end if not r0 then return false end if fuzzyCompare(math.abs(r10), 1) then if r1 then return false end r1 = true elseif not fuzzyCompare(r10, 0) then return false end if fuzzyCompare(math.abs(r11), 1) then if r1 then return false end r1 = true elseif not fuzzyCompare(r11, 0) then return false end if fuzzyCompare(math.abs(r12), 1) then if r1 then return false end r1 = true elseif not fuzzyCompare(r12, 0) then return false end if not r1 then return false end if fuzzyCompare(math.abs(r20), 1) then if r2 then return false end r2 = true elseif not fuzzyCompare(r20, 0) then return false end if fuzzyCompare(math.abs(r21), 1) then if r2 then return false end r2 = true elseif not fuzzyCompare(r21, 0) then return false end if fuzzyCompare(math.abs(r22), 1) then if r2 then return false end r2 = true elseif not fuzzyCompare(r22, 0) then return false end if not r2 then return false end return true end local module = {} module.fuzzyCompare = fuzzyCompare module.fuzzyCompareVector3 = fuzzyCompareVector3 module.visiblyIdentityCFrame = visiblyIdentityCFrame return module ]]> Round Extent upperV.x then upperV = Vector3.new(vect.x, upperV.y, upperV.z) end if vect.y > upperV.y then upperV = Vector3.new(upperV.x, vect.y, upperV.z) end if vect.z > upperV.z then upperV = Vector3.new(upperV.x, upperV.y, vect.z) end lowerV = extentSpace:pointToWorldSpace(lowerV) upperV = extentSpace:pointToWorldSpace(upperV) return lowerV, upperV end function unionTuple(bounds, lowerBounds, upperBounds, extentSpace) if not bounds.Lower and not bounds.Upper then return {Lower=nil, Upper=nil} end if not lowerBounds then lowerBounds = bounds.Lower end if not upperBounds then upperBounds = bounds.Upper end lowerBounds = Utility.minVector3(bounds.Lower, lowerBounds) upperBounds = Utility.maxVector3(bounds.Upper, upperBounds) return {Lower=lowerBounds, Upper=upperBounds} end local level = 0 function unionVector3NoSpaceChange(vect, lowerV, upperV) if not vect then return lowerV, upperV end if not lowerV then lowerV = vect end if not upperV then upperV = vect end if vect.x < lowerV.x then lowerV = Vector3.new(vect.x, lowerV.y, lowerV.z) end if vect.y < lowerV.y then lowerV = Vector3.new(lowerV.x, vect.y, lowerV.z) end if vect.z < lowerV.z then lowerV = Vector3.new(lowerV.x, lowerV.y, vect.z) end if vect.x > upperV.x then upperV = Vector3.new(vect.x, upperV.y, upperV.z) end if vect.y > upperV.y then upperV = Vector3.new(upperV.x, vect.y, upperV.z) end if vect.z > upperV.z then upperV = Vector3.new(upperV.x, upperV.y, vect.z) end return lowerV, upperV end function getPartBounds(object, currentSpace) -- traced usge of getPartBounds. This'll never be nil. -- on another note: extentSpace doesn't exist in this scope... --if not currentSpace then currentSpace = extentSpace end local halfSize = object.Size / 2 local lowerBounds, upperBounds lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,-1,-1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,-1,1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,1,-1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,1,1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,-1,-1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,-1,1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,1,-1)), lowerBounds, upperBounds,currentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,1,1)), lowerBounds, upperBounds,currentSpace) return lowerBounds, upperBounds end function getExtentsRecursive(object, extentSpace) level = level + 1 local lowerBounds = nil local upperBounds = nil local children = object:GetChildren() for _,part in ipairs(children) do --local bounds = unionTuple(getExtentsRecursive(part, extentSpace), lowerBounds, upperBounds, extentSpace) local bounds = getExtentsRecursive(part, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Lower, lowerBounds, upperBounds, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Upper, lowerBounds, upperBounds, extentSpace) end if object:IsA("BasePart") then local halfSize = object.Size / 2 lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,-1,-1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,-1,1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,1,-1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1,1,1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,-1,-1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,-1,1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,1,-1)), lowerBounds, upperBounds,extentSpace) lowerBounds, upperBounds = unionVector3(object.CFrame:pointToWorldSpace(halfSize * Vector3.new(1,1,1)), lowerBounds, upperBounds,extentSpace) end level = level - 1 return {Lower = lowerBounds, Upper = upperBounds} end local function getExtentsOfSelection(extentSpace) local selection = game:GetService("Selection"):Get() local lowerBounds = nil local upperBounds = nil for i,v in ipairs(selection) do if v.Parent and v.Parent.Parent and v:IsDescendantOf(workspace) then local bounds = getExtentsRecursive(v, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Lower, lowerBounds, upperBounds, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Upper, lowerBounds, upperBounds, extentSpace) end end if not lowerBounds or not upperBounds then lowerBounds = extentSpace.p upperBounds = extentSpace.p end return lowerBounds, upperBounds end local Fuzzy = require(script.Parent.FuzzyMath) function getExtentsOfList(list, extentSpace) local selection = list local lowerBounds = nil local upperBounds = nil for i,v in ipairs(selection) do local bounds = getExtentsRecursive(v, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Lower, lowerBounds, upperBounds, extentSpace) lowerBounds, upperBounds = unionVector3(bounds.Upper, lowerBounds, upperBounds, extentSpace) end if not lowerBounds then lowerBounds = Vector3.new(0,0,0) end if not upperBounds then upperBounds = Vector3.new(0,0,0) end lowerBounds = extentSpace:pointToObjectSpace(lowerBounds) upperBounds = extentSpace:pointToObjectSpace(upperBounds) return {Lower=lowerBounds, Upper=upperBounds} end function getExtents(extentSpace) local lowerBounds, upperBounds = getExtentsOfSelection(extentSpace) lowerBounds = extentSpace:pointToObjectSpace(lowerBounds) upperBounds = extentSpace:pointToObjectSpace(upperBounds) return {Lower=lowerBounds, Upper=upperBounds} end function extentsToCFrameAndSize(extents, extentSpace) if not extents.Lower or not extents.Upper then return nil, nil end local size = Utility.absVector3(extents.Upper - extents.Lower) local position = extents.Lower + (size / 2) return extentSpace:toWorldSpace(CFrame.new(position)), size end function setPartCFrameToExtents(part, space) local extentSpace if space then extentSpace = space else extentSpace = CFrame.new(Vector3.new(0,0,0)) end local cframe, size = extentsToCFrameAndSize(getExtents(extentSpace), extentSpace) if size then part.Size = size end if cframe then part.CFrame = cframe end end local function getCFrameOfList(list, extentSpace) local extents = getExtentsOfList(list, extentSpace) local cframe = extentsToCFrameAndSize(extents, extentSpace) return cframe end local function getSizeOfList(list, extentSpace) local extents = getExtentsOfList(list, extentSpace) local cframe, size = extentsToCFrameAndSize(extents,extentSpace) return size end local function getCFrameAndSizeOfList(list, extentSpace) local extents = getExtentsOfList(list, extentSpace) return extentsToCFrameAndSize(extents,extentSpace) end local module = {} module.setPartCFrameToExtents = setPartCFrameToExtents module.unionVector3NoSpaceChange = unionVector3NoSpaceChange module.getPartBounds = getPartBounds module.getCFrameOfList = getCFrameOfList module.getSizeOfList = getSizeOfList module.getCFrameAndSizeOfList = getCFrameAndSizeOfList return module ]]> Adornments 0 if flip then direction = direction * -1 end local originalRadius = radius if not internalRotate then if direction > 0 then degrees = math.floor(degrees) else degrees = math.ceil(degrees) end radius = radius * 1.15 end local amount = math.abs(degrees) --local pos = cframe:toWorldSpace(CFrame.Angles(flip and math.rad(180) or 0,0, math.rad((amount * direction)+ 45)) * CFrame.new(Vector3.new(-originalRadius, originalRadius, 0))) --setTextVisible1(true, game.Workspace.Camera:WorldToScreenPoint(pos.p)) --if degrees == 0 then return end while amount >= 90 do --add 90 if direction < 0 then amount = amount - 90 end local im = Instance.new("ImageHandleAdornment", cg) im.Adornee = strayAdornee im.Image = ninetyDegrees im.CFrame = cframe:toWorldSpace(CFrame.Angles(flip and math.rad(180) or 0,0, math.rad(amount * direction)) * CFrame.new(Vector3.new(-radius * 0.5, radius * 0.5, 0))) im.ZIndex = 1 im.AlwaysOnTop = true im.Size = Vector2.new(radius, radius) im.Color3 = Color3.new(1, 0, 0) im.Transparency = 0.6 table.insert(tmpRotation, im) if direction > 0 then amount = amount - 90 end end while internalRotate and amount >= 22.5 do --add 90 if direction < 0 then amount = amount - 22.5 end local im = Instance.new("ImageHandleAdornment", cg) im.Adornee = strayAdornee im.Image = oneEigthDegrees im.CFrame = cframe:toWorldSpace(CFrame.Angles(flip and math.rad(180) or 0,0, math.rad(amount * direction)) * CFrame.new(Vector3.new(-radius * 0.5, radius * 0.5, 0))) im.ZIndex = 1 im.AlwaysOnTop = true im.Size = Vector2.new(radius, radius) im.Color3 = Color3.new(1, 0, 0) im.Transparency = 0.6 table.insert(tmpRotation, im) if direction > 0 then amount = amount - 22.5 end end while amount >= 5 do if direction < 0 then amount = amount - 5 end local im = Instance.new("ImageHandleAdornment", cg) im.Adornee = strayAdornee im.Image = fiveDegrees im.CFrame = cframe:toWorldSpace(CFrame.Angles(flip and math.rad(180) or 0,0, math.rad(amount * direction)) * CFrame.new(Vector3.new(-radius * 0.5, radius * 0.5, 0))) im.ZIndex = 1 im.AlwaysOnTop = true im.Size = Vector2.new(radius, radius) im.Color3 = Color3.new(1, 0, 0) im.Transparency = 0.6 table.insert(tmpRotation, im) if direction > 0 then amount = amount - 5 end end while amount >= 1 do if direction < 0 then amount = amount - 1 end local im = Instance.new("ImageHandleAdornment", cg) im.Adornee = strayAdornee im.Image = oneDegrees im.CFrame = cframe:toWorldSpace(CFrame.Angles(flip and math.rad(180) or 0,0, math.rad(amount * direction)) * CFrame.new(Vector3.new(-radius * 0.5, radius * 0.5, 0))) im.ZIndex = 1 im.AlwaysOnTop = true im.Size = Vector2.new(radius, radius) im.Color3 = Color3.new(1, 0, 0) im.Transparency = 0.6 table.insert(tmpRotation, im) if direction > 0 then amount = amount - 1 end end end local rotationFrame = nil local rotationRadius = nil local rotationDegrees = nil local function setRotatePosition(cframe, radius, degrees) renderRotation = true local largeLineLength = radius / 10 local smallLineLength = largeLineLength / 2 for i = 1, 72 do rotateLines[i].Length = ((i - 1) % 9 == 0) and largeLineLength or smallLineLength local angle = (i - 1) * 5 local direction = Vector3.new(math.cos(math.rad(angle)), math.sin(math.rad(angle), 0)) * radius rotateLines[i].Transparency = internalRotate and .4 or 0 rotateLines[i].CFrame = cframe:toWorldSpace(CFrame.new(direction, direction * 2)) end for i = 73, 88 do rotateLines[i].Length = largeLineLength local angle = (i - 73) * 22.5 local direction = Vector3.new(math.cos(math.rad(angle)), math.sin(math.rad(angle), 0)) * radius rotateLines[i].Transparency = internalRotate and 0 or .4 rotateLines[i].CFrame = cframe:toWorldSpace(CFrame.new(direction, direction * 0.5)) end rotationFrame = cframe rotationRadius = radius rotationDegrees = degrees end local startRadius = 0.3 local function setPlaneFrame(cframe) planeOriginLines[1].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(0, startRadius, 0), Vector3.new(0, 2, 0))) planeOriginLines[2].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(0, -startRadius, 0), Vector3.new(0, -2, 0))) planeOriginLines[3].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(startRadius, 0, 0), Vector3.new(2, 0, 0))) planeOriginLines[4].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(-startRadius, 0, 0), Vector3.new(-2, 0, 0))) planeOriginLines[5].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(0, 0, startRadius), Vector3.new(0, 0, 2))) planeOriginLines[6].CFrame = cframe:toWorldSpace(CFrame.new(Vector3.new(0, 0, -startRadius), Vector3.new(0, 0, -2))) end local initialPlaneClick = false local function hoverEnterHandle(handle) hoveredHandles[handle] = true if not adornKeeper[handle][2].Visible or Input.getButtonState(Input.Enum.Key.MOUSE_BUTTON1) then return end if handle == currentHandle then currentlyOverHandle = true end if handle >= currentHandle and currentHandle ~= H_NONE then return end if handle == H_PLANE then planeHoverTransition = true else if not planeSelectionActive then adornKeeper[handle][2].Color3 = adornKeeper[handle][4][1] adornKeeper[handle][2].Transparency = adornKeeper[handle][4][2] if shadowKeeper[handle] then shadowKeeper[handle].Color3 = adornKeeper[handle][4][1] shadowKeeper[handle].Transparency = shadowTransparencyHover end end end currentHandle = handle currentlyOverHandle = true end local function resetHandle(handle) adornKeeper[handle][2].Color3 = adornKeeper[handle][5][1] adornKeeper[handle][2].Transparency = adornKeeper[handle][5][2] if shadowKeeper[handle] then shadowKeeper[handle].Color3 = Color3.new(0, 0, 0) shadowKeeper[handle].Transparency = shadowTransparency end end local function hoverLeaveHandle(handle) hoveredHandles[handle] = nil if handle == currentHandle then currentlyOverHandle = false end if Input.getButtonState(Input.Enum.Key.MOUSE_BUTTON1) then return end if handle == H_PLANE then planeHoverTransition = false else resetHandle(handle) end local finalHandle = H_NONE for k, v in pairs(hoveredHandles) do if v then finalHandle = k end end currentHandle = H_NONE if finalHandle ~= H_NONE then hoverEnterHandle(finalHandle) end end local function planeInputBegan(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then initialPlaneClick = true end end local function planeInputEnded(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then if currentHandle == H_PLANE and initialPlaneClick then planeSelectionActive = not planeSelectionActive end initialPlaneClick = false end end local function InitializeBoxAdorn(offset, size, color, top, zIndex) if zIndex == nil then zIndex = 0 end local tmpBoxAdorn = Instance.new("BoxHandleAdornment", cg) tmpBoxAdorn.SizeRelativeOffset = offset tmpBoxAdorn.Size = size tmpBoxAdorn.Color3 = color tmpBoxAdorn.AlwaysOnTop = top tmpBoxAdorn.ZIndex = zIndex return tmpBoxAdorn end local function InitializeImageAdorn(offset, size, color, top, zIndex, cFrame, visible, image) local imageAdorn = Instance.new("ImageHandleAdornment", cg) imageAdorn.Image = image imageAdorn.SizeRelativeOffset = offset imageAdorn.Size = size imageAdorn.CFrame = cFrame imageAdorn.Color3 = color[1] imageAdorn.ZIndex = zIndex imageAdorn.Transparency = color[2] imageAdorn.AlwaysOnTop = top imageAdorn.Visible = visible return imageAdorn end local function InitializeConeAdorn(offset, size, color, top, zIndex, cFrame) local coneAdorn = Instance.new("ConeHandleAdornment", cg) coneAdorn.SizeRelativeOffset = offset coneAdorn.CFrame = cFrame coneAdorn.Height = size.X coneAdorn.Radius = size.Y coneAdorn.Color3 = color[1] coneAdorn.ZIndex = zIndex coneAdorn.AlwaysOnTop = top return coneAdorn end local lightBlue = Color3.new(38.0 / 255.0, 136.0 / 255.0, 240.0 / 255.0) local darkBlue = Color3.new(21/255, 26/255, 89/255) local black = Color3.new(0, 0, 0) local white = Color3.new(1, 1, 1) local yellow = Color3.new(239/255,230/255,64/255) local grey = Color3.new(184/255, 184/255, 184/255) local rbxBlue = Color3.new(0, 162/255, 1) local rbxGreen = Color3.new(63/255, 198/255, 121/255) local rbxRed = Color3.new(226/255,35/255,26/255) local red = BrickColor.Red().Color local BOX_ADORN = 0 local OUTLINE_BOX_ADORN = 1 local CONE_ADORN = 2 local IMAGE_ADORN = 3 local function createAdorn(type, name, hoverColor, offColor, outerSize, innerSize, offset, cframe, image) if cframe == nil then cframe = CFrame.new() end if type == BOX_ADORN then local boxAdorn = InitializeBoxAdorn(offset, outerSize, offColor[1], true, 2) local shadow = InitializeBoxAdorn(offset, outerSize, black, true, 1) shadow.Transparency = shadowTransparency boxAdorn.MouseEnter:connect(function() hoverEnterHandle(name) end) boxAdorn.MouseLeave:connect(function() hoverLeaveHandle(name) end) adornKeeper[name] = {boxAdorn, boxAdorn, name, hoverColor, offColor, outerSize, innerSize} shadowKeeper[name] = shadow elseif type == OUTLINE_BOX_ADORN then local boxAdornSmall = InitializeBoxAdorn(offset, innerSize, offColor[1], true, 3) local boxAdornLarge = InitializeBoxAdorn(offset, outerSize, black, true, 2) local shadow = InitializeBoxAdorn(offset, outerSize, black, true, 1) shadow.Transparency = shadowTransparency boxAdornLarge.MouseEnter:connect(function() hoverEnterHandle(name) end) boxAdornLarge.MouseLeave:connect(function() hoverLeaveHandle(name) end) adornKeeper[name] = {boxAdornLarge, boxAdornSmall, name, hoverColor, offColor, outerSize, innerSize} shadowKeeper[name] = shadow elseif type == CONE_ADORN then local coneAdornSmall = InitializeConeAdorn(offset, innerSize, offColor, true, 2, cframe) local coneAdornLarge = InitializeConeAdorn(offset, outerSize, {black, 0}, true, 1, cframe - Vector3.new(0, 0.05, 0)) coneAdornLarge.MouseEnter:connect(function() hoverEnterHandle(T_Y_POS) end) coneAdornLarge.MouseLeave:connect(function() hoverLeaveHandle(T_Y_POS) end) adornKeeper[name] = {coneAdornLarge, coneAdornSmall, name, hoverColor, offColor, outerSize, innerSize} elseif type == IMAGE_ADORN then local imageAdorn = InitializeImageAdorn(offset, outerSize, offColor, true, 6, cframe, true, image) local imageAdornShadow = InitializeImageAdorn(offset, innerSize, {black, 0}, true, 2, cframe, false, image) if name == R_XZ then imageAdorn.Name = "R_XY" end imageAdorn.MouseEnter:connect(function() hoverEnterHandle(name) end) imageAdorn.MouseLeave:connect(function() hoverLeaveHandle(name) end) adornKeeper[name] = {imageAdornShadow, imageAdorn , name, hoverColor, offColor, outerSize, innerSize} end end local function initializeAdorns() --translation createAdorn(CONE_ADORN, T_Y_POS, {red, 0}, {yellow, 0}, Vector3.new(.8, .3, 0), Vector3.new(.6, .21, 0), Vector3.new(0, 1, 0), CFrame.new(Vector3.new(0, 3.05, 0), Vector3.new(0, 5, 0))) --cardinal createAdorn(BOX_ADORN, S_X_POS, {red, 0}, {darkBlue, 0}, Vector3.new(.1,.1,.1), Vector3.new(.1,.1,.1), Vector3.new(1, -1, 0)) createAdorn(BOX_ADORN, S_X_NEG, {red, 0}, {darkBlue, 0}, Vector3.new(.1,.1,.1), Vector3.new(.1,.1,.1), Vector3.new(-1, -1, 0)) createAdorn(BOX_ADORN, S_Z_POS, {red, 0}, {darkBlue, 0}, Vector3.new(.1,.1,.1), Vector3.new(.1,.1,.1), Vector3.new(0, -1, 1)) createAdorn(BOX_ADORN, S_Z_NEG, {red, 0}, {darkBlue, 0}, Vector3.new(.1,.1,.1), Vector3.new(.1,.1,.1), Vector3.new(0, -1, -1)) --ordinal createAdorn(OUTLINE_BOX_ADORN, S_X_POS_Z_POS, {red, 0}, {white, 0}, Vector3.new(.15,.15,.15), Vector3.new(.1,.1,.1), Vector3.new(1, -1, 1)) createAdorn(OUTLINE_BOX_ADORN, S_X_POS_Z_NEG, {red, 0}, {white, 0}, Vector3.new(.15,.15,.15), Vector3.new(.1,.1,.1), Vector3.new(1, -1, -1)) createAdorn(OUTLINE_BOX_ADORN, S_X_NEG_Z_POS, {red, 0}, {white, 0}, Vector3.new(.15,.15,.15), Vector3.new(.1,.1,.1), Vector3.new(-1, -1, 1)) createAdorn(OUTLINE_BOX_ADORN, S_X_NEG_Z_NEG, {red, 0}, {white, 0}, Vector3.new(.15,.15,.15), Vector3.new(.1,.1,.1), Vector3.new(-1, -1, -1)) createAdorn(OUTLINE_BOX_ADORN, S_Y_POS, {red, 0}, {white, 0}, Vector3.new(.15,.15,.15), Vector3.new(.1,.1,.1), Vector3.new(0, 1, 0)) --rotation createAdorn(IMAGE_ADORN, R_XY, {rbxBlue, 0}, {rbxBlue, .3}, Vector2.new(1, 1), Vector2.new(1, 1), Vector3.new(-1, 1, -1), CFrame.new(Vector3.new(0,0,0), Vector3.new(0, 0, 1)), rotationArrow) createAdorn(IMAGE_ADORN, R_XZ, {rbxGreen, 0}, {rbxGreen, .3}, Vector2.new(1, 1), Vector2.new(1, 1), Vector3.new(-1, 1, 1), CFrame.new(Vector3.new(0,0,0), Vector3.new(0, 1, 0)), rotationArrow) createAdorn(IMAGE_ADORN, R_YZ, {rbxRed, 0}, {rbxRed, .3}, Vector2.new(1, 1), Vector2.new(1, 1), Vector3.new(1, 1, 1), CFrame.new(Vector3.new(0,0,0), Vector3.new(1, 0, 0)), rotationArrow) local planeAdorn = Instance.new("ImageHandleAdornment", cg) planeAdorn.Image = planeImage planeAdorn.Visible = false local parentScreen = Instance.new("ScreenGui", cg) parentScreen.Name = "PrecisionDraggerGui" local imageLabel = Instance.new("ImageLabel", parentScreen) --imageLabel.Visible = false imageLabel.Image = planeImage imageLabel.BackgroundTransparency = 1 imageLabel.BorderSizePixel = 0 imageLabel.ImageColor3 = black imageLabel.Size = UDim2.new(0, 80, 0, 80) imageLabel.Position = UDim2.new(0, 5, 0, 5) imageLabel.ImageTransparency = 0.5--0.9 imageLabel.MouseEnter:connect(function() hoverEnterHandle(H_PLANE) end) imageLabel.MouseLeave:connect(function() hoverLeaveHandle(H_PLANE) end) imageLabel.InputBegan:connect(function(input) planeInputBegan(input) end) imageLabel.InputEnded:connect(function(input) planeInputEnded(input) end) adornKeeper[H_PLANE] = {imageLabel, imageLabel, H_PLANE, {grey, 0}, {black, 0.5}, Vector2.new(1, 1), Vector2.new(1, 1)} rotateAxisLine[1] = Instance.new("LineHandleAdornment", cg) rotateAxisLine[1].Visible = false rotateAxisLine[1].Color3 = rbxRed rotateAxisLine[1].Thickness = 3 rotateAxisLine[1].ZIndex = 5 rotateAxisLine[2] = rotateAxisLine[1]:Clone() rotateAxisLine[2].Parent = cg rotateAxisLine[2].Color3 = rbxGreen rotateAxisLine[3] = rotateAxisLine[1]:Clone() rotateAxisLine[3].Parent = cg rotateAxisLine[3].Color3 = rbxBlue --measure if not strayAdornee then measureFrame1 = Instance.new("Frame", parentScreen) measureFrame1.Visible = false measureFrame1.Name = "MeasureFrame" measureFrame1.BackgroundTransparency = 0.45 measureFrame1.BorderSizePixel = 0 measureFrame1.BackgroundColor3 = white measureFrame2 = measureFrame1:Clone() measureFrame2.Parent = parentScreen measureLabel1 = Instance.new("TextLabel", measureFrame1) measureLabel1.Name = "MeasureLabel" measureLabel1.Font = Enum.Font.ArialBold measureLabel1.FontSize = Enum.FontSize.Size24 measureLabel1.BackgroundTransparency = 1.0 measureLabel1.BorderSizePixel = 0 measureLabel2 = measureLabel1:Clone() measureLabel2.Parent = measureFrame2 unitLabel1 = Instance.new("TextLabel", measureFrame1) unitLabel1.Name = "UnitLabel" unitLabel1.Font = Enum.Font.ArialBold unitLabel1.FontSize = Enum.FontSize.Size14 unitLabel1.BackgroundTransparency = 1.0 unitLabel1.BorderSizePixel = 0 unitLabel2 = unitLabel1:Clone() unitLabel2.Parent = measureFrame2 strayAdornee = Instance.new("Part", game.CoreGui) strayAdornee.Anchored = true strayAdornee.CFrame = CFrame.new() measureLine1 = Instance.new("LineHandleAdornment", cg) measureLine1.Visible = false measureLine1.Color3 = black measureLine1.Adornee = strayAdornee measureLine1.AlwaysOnTop = true measureLine1.ZIndex = 5 measureLine2 = measureLine1:Clone() measureLine2.Parent = cg measureLine3 = measureLine1:Clone() measureLine3.Parent = cg measureLine4 = measureLine1:Clone() measureLine4.Parent = cg measureLine5 = measureLine1:Clone() measureLine5.Parent = cg measureLine6 = measureLine1:Clone() measureLine6.Parent = cg planeOriginLines[1] = measureLine1:Clone() planeOriginLines[1].Color3 = yellow planeOriginLines[1].Parent = cg planeOriginLines[1].Thickness = 2 planeOriginLines[1].Length = 0.6 for i = 2, 6 do planeOriginLines[i] = planeOriginLines[1]:Clone() planeOriginLines[i].Parent = cg end measureCone1 = Instance.new("ConeHandleAdornment", cg) measureCone1.Visible = false measureCone1.Color3 = black measureCone1.Adornee = strayAdornee measureCone1.Height = 0.5 measureCone1.Radius = 0.1 measureCone1.AlwaysOnTop = true measureCone1.ZIndex = 5 measureCone2 = measureCone1:Clone() measureCone2.Parent = cg measureCone3 = measureCone1:Clone() measureCone3.Parent = cg measureCone4 = measureCone1:Clone() measureCone4.Parent = cg -- mini plane -- miniPlaneLine1 = Instance.new("ImageHandleAdornment", cg) -- miniPlaneLine1.Visible = false -- miniPlaneLine1.Color3 = white -- miniPlaneLine1.Adornee = strayAdornee -- miniPlaneLine1.AlwaysOnTop = true -- miniPlaneLine1.ZIndex = 10 miniPlaneLines[1] = Instance.new("ImageLabel", parentScreen) miniPlaneLines[1].Visible = false --miniPlaneLines[1].BackgroundColor3 = white miniPlaneLines[1].BackgroundTransparency = 1 miniPlaneLines[1].BorderSizePixel = 0 miniPlaneLines[1].Image = gradient -- miniPlaneLines[1].Size = UDim2.new(0,10,0,10) -- miniPlaneLines[1].Position = UDim2.new(0, 100, 0, 100) for i = 2, 8 do miniPlaneLines[i] = miniPlaneLines[1]:Clone() miniPlaneLines[i].Parent = parentScreen end end ---measure rotate lines for i = 1, 88 do local rotateLine = Instance.new("LineHandleAdornment", game.CoreGui) rotateLine.Visible = false rotateLine.Adornee = strayAdornee rotateLine.AlwaysOnTop = true rotateLine.ZIndex = 2 rotateLine.Color3 = black rotateLine.Thickness = 2 table.insert(rotateLines, rotateLine) end -------- local line1 = Instance.new("LineHandleAdornment", cg) line1.Length = 2 line1.Color3 = black line1.SizeRelativeOffset = Vector3.new(0, 1, 0) line1.CFrame = CFrame.new(Vector3.new(0, -1, 0), Vector3.new(0, -1, 0)) line1.Thickness = 2 planeKeeper[1] = line1 planeKeeper[2] = line1:Clone() planeKeeper[2].Parent = cg planeKeeper[3] = line1:Clone() planeKeeper[3].Parent = cg planeKeeper[4] = line1:Clone() planeKeeper[4].Parent = cg initialized = true end local function setAdornVisibility(adorn, value) adornKeeper[adorn][1].Visible = value adornKeeper[adorn][2].Visible = value if shadowKeeper[adorn] then shadowKeeper[adorn].Visible = value end end local function setTextLocation1(location) if location then measureFrame1.Position = UDim2.new(0, location.X, 0, location.Y) end end local function setTextLocation2(location) if location then measureFrame2.Position = UDim2.new(0, location.X, 0, location.Y) end end function setTextVisible1(value, location) setTextLocation1(location) measureFrame1.Visible = value end local function setTextVisible2(value, location) setTextLocation2(location) measureFrame2.Visible = value end local function setSizeLineVisible1(value) measureLine1.Visible = value measureLine2.Visible = value measureLine3.Visible = value measureCone1.Visible = value measureCone2.Visible = value end local function setSizeLineVisible2(value) measureLine4.Visible = value measureLine5.Visible = value measureLine6.Visible = value measureCone3.Visible = value measureCone4.Visible = value end local function setRotateVisible(value) for i, v in ipairs(rotateLines) do v.Visible = value end end local function setPlaneCFrameVisible(value) for i = 1, 6 do planeOriginLines[i].Visible = value end end local function adornInstance(adorn, adornee) adornKeeper[adorn][1].Adornee = adornee adornKeeper[adorn][2].Adornee = adornee if shadowKeeper[adorn] then shadowKeeper[adorn].Adornee = adornee end end local function setPlaneVisibility(value) for i, v in ipairs(planeKeeper) do v.Visible = value end setAdornVisibility(H_PLANE, value) for i = 1, #miniPlaneLines do miniPlaneLines[i].Visible = value end end local function adornInstanceWithPlane(adornee) for i, v in ipairs(planeKeeper) do v.Adornee = adornee end end local function setTranslateAdornVisibility(value) setAdornVisibility(T_Y_POS, value) end local function adornInstanceWithTranslate(adornee) adornInstance(T_Y_POS, adornee) end local function setScaleAdornVisibility(value) setAdornVisibility(S_X_POS, value) setAdornVisibility(S_X_NEG, value) setAdornVisibility(S_Z_POS, value) setAdornVisibility(S_Z_NEG, value) setAdornVisibility(S_Y_POS, value) setAdornVisibility(S_X_POS_Z_POS, value) setAdornVisibility(S_X_POS_Z_NEG, value) setAdornVisibility(S_X_NEG_Z_POS, value) setAdornVisibility(S_X_NEG_Z_NEG, value) end local function adornInstanceWithScale(adornee) adornInstance(S_X_POS, adornee) adornInstance(S_X_NEG, adornee) adornInstance(S_Z_POS, adornee) adornInstance(S_Z_NEG, adornee) adornInstance(S_Y_POS, adornee) adornInstance(S_X_POS_Z_POS, adornee) adornInstance(S_X_POS_Z_NEG, adornee) adornInstance(S_X_NEG_Z_POS, adornee) adornInstance(S_X_NEG_Z_NEG, adornee) end local function setRotateAdornVisibility(value) setAdornVisibility(R_XY, value) setAdornVisibility(R_XZ, value) setAdornVisibility(R_YZ, value) end local function adornInstanceWithRotate(adornee) adornInstance(R_XY, adornee) adornInstance(R_XZ, adornee) adornInstance(R_YZ, adornee) end local tempPoints = {} local function clearTemporaryLines() for i, v in ipairs(tempPoints) do v.Visible = false v.Adornee = nil v.Parent = nil v:Destroy() tempPoints[i] = nil end end local function drawTemporaryLine(startPoint, endPoint, transparency, thickness, adornee) if not adornee then return end local tmp = Instance.new("LineHandleAdornment", cg) local objectStartPoint = adornee.CFrame:pointToObjectSpace(startPoint) local objectEndPoint = adornee.CFrame:pointToObjectSpace(endPoint) local direction = (objectEndPoint - objectStartPoint) tmp.Length = direction.Magnitude tmp.CFrame = CFrame.new(objectStartPoint, objectEndPoint) local transition = Utility.smoothstep(0, 1, currentPlaneHoverTransitionLevel) tmp.Color3 = Utility.colorAdd(Utility.colorMultiply(grey, transition), Utility.colorMultiply(black, 1-transition)) tmp.Transparency = transparency tmp.Thickness = (thickness * 1.2 * (transition)) + (thickness * (1-transition)) tmp.ZIndex = 1 tmp.Adornee = adornee table.insert(tempPoints, tmp) end local function setFrameLine(index, point1, point2) local length = (point2 - point1).magnitude + 1 local midPoint = (point1 + point2) * 0.5 miniPlaneLines[index].Rotation = math.deg(math.atan2(point2.Y - point1.Y, point2.X - point1.X)) miniPlaneLines[index].Size = UDim2.new(0, length, 0, 3) miniPlaneLines[index].Position = UDim2.new(0, midPoint.X - length * 0.5, 0, midPoint.Y) miniPlaneLines[index].Visible = true end local function updateMiniPlane(planeFrame) local camera = game.Workspace.Camera local size = camera.ViewportSize local sizeMultiplier = 10 local screenDiff = -planeFrame.p + camera.CoordinateFrame.p + (camera:ScreenPointToRay(size.x * 0.5, size.y * 0.5).Direction * 60* sizeMultiplier * 0.5) local point1 = planeFrame:pointToWorldSpace(Vector3.new(-sizeMultiplier, 0, -sizeMultiplier)) + screenDiff local point2 = planeFrame:pointToWorldSpace(Vector3.new(sizeMultiplier, 0, -sizeMultiplier)) + screenDiff local point3 = planeFrame:pointToWorldSpace(Vector3.new(-sizeMultiplier, 0, sizeMultiplier)) + screenDiff local point4 = planeFrame:pointToWorldSpace(Vector3.new(sizeMultiplier, 0, sizeMultiplier)) + screenDiff local p1ss = camera:WorldToScreenPoint(point1) local p2ss = camera:WorldToScreenPoint(point2) local p3ss = camera:WorldToScreenPoint(point3) local p4ss = camera:WorldToScreenPoint(point4) local maxSize = 65 local highestDiff = math.max((p1ss - p4ss).Magnitude, (p2ss - p3ss).Magnitude) local difference = maxSize / highestDiff local midpoint = size * 0.5 local centerv3 = Vector3.new(midpoint.x, midpoint.y, 0) local midpointDiff = Vector2.new(45, 45) - Vector2.new(midpoint.x, midpoint.y) p1ss = centerv3 + ((p1ss - centerv3) * difference) p2ss = centerv3 + ((p2ss - centerv3) * difference) p3ss = centerv3 + ((p3ss - centerv3) * difference) p4ss = centerv3 + ((p4ss - centerv3) * difference) local screenPoint1 = Vector2.new(p1ss.x + midpointDiff.X, p1ss.y + midpointDiff.Y) local screenPoint2 = Vector2.new(p2ss.x + midpointDiff.X, p2ss.y + midpointDiff.Y) local screenPoint3 = Vector2.new(p3ss.x + midpointDiff.X, p3ss.y + midpointDiff.Y) local screenPoint4 = Vector2.new(p4ss.x + midpointDiff.X, p4ss.y + midpointDiff.Y) local screenPoint12 = ((screenPoint1 * 2) / 3) + (screenPoint2 / 3) local screenPoint21 = ((screenPoint2 * 2) / 3) + (screenPoint1 / 3) local screenPoint13 = ((screenPoint1 * 2) / 3) + (screenPoint3 / 3) local screenPoint31 = ((screenPoint3 * 2) / 3) + (screenPoint1 / 3) local screenPoint24 = ((screenPoint2 * 2) / 3) + (screenPoint4 / 3) local screenPoint42 = ((screenPoint4 * 2) / 3) + (screenPoint2 / 3) local screenPoint34 = ((screenPoint3 * 2) / 3) + (screenPoint4 / 3) local screenPoint43 = ((screenPoint4 * 2) / 3) + (screenPoint3 / 3) setFrameLine(1, screenPoint1, screenPoint2) setFrameLine(2, screenPoint2, screenPoint4) setFrameLine(3, screenPoint4, screenPoint3) setFrameLine(4, screenPoint3, screenPoint1) setFrameLine(5, screenPoint12, screenPoint34) setFrameLine(6, screenPoint21, screenPoint43) setFrameLine(7, screenPoint13, screenPoint24) setFrameLine(8, screenPoint31, screenPoint42) end local function updatePlanePosition() local workplaneFrame, workplaneOffset = workplaneAccessor() if not planeKeeper[1].Visible and not renderRotation then return end -- temporary internal lines --if not workplaneFrame then return end --drawTemporaryLine(Vector3.new(0,0,0), Vector3.new(10, 10, 0), Color3.new(.3,.3,.3), 0.0, 1.5, planeKeeper[1].Adornee) --if true then return end local adornee = planeKeeper[1].Adornee local halfSize = adornee.Size / 2 local p0 = {} local p1 = {} local camera_pos = Input.getMouseLocation() local distFromCamera = (camera_pos - adornee.CFrame:pointToWorldSpace(Vector3.new(0, -workplaneOffset, 0))).Magnitude; local minPosition = ((workplaneFrame:toObjectSpace(CFrame.new(adornee.CFrame:pointToWorldSpace(-halfSize)))).p) * Vector3.new(1, 0, 1); local maxPosition = ((workplaneFrame:toObjectSpace(CFrame.new(adornee.CFrame:pointToWorldSpace(halfSize)))).p) * Vector3.new(1, 0, 1); local minX = math.floor(math.min(minPosition.x, maxPosition.x)); local maxX = math.ceil(math.max(minPosition.x, maxPosition.x)); local minZ = math.floor(math.min(minPosition.z, maxPosition.z)); local maxZ = math.ceil(math.max(minPosition.z, maxPosition.z)); p0[1] = 0; p1[1] = 0; local minVisibleDist = 50; local maxVisibleDist = 100; local minFarDist = 250; local maxFarDist = 400; --setUnitText("34.56", TYPE_STUDS) --setLinePosition(Vector3.new(0,0,0), Vector3.new(3, 5, 7)) --setSizeLineVisible1(true) --setTextVisible(true, Vector2.new(25, 300)) local transparencyDist = (-1 / (maxVisibleDist - minVisibleDist) * distFromCamera) + (minVisibleDist / (maxVisibleDist - minVisibleDist)) + 1; local farTransparency = (-1 / (maxFarDist - minFarDist) * distFromCamera) + (minFarDist / (maxFarDist - minFarDist)) + 1; transparencyDist = math.max(math.min(transparencyDist, 0.9), 0.0) farTransparency = math.max(math.min(farTransparency, 1.0), 0.0) --local totalLines = 10 if renderRotation then for i, v in ipairs(planeKeeper) do v.Visible = false end if rotationFrame then setRotation(rotationFrame, rotationRadius, rotationDegrees) end else planeKeeper[1].SizeRelativeOffset = Vector3.new(1, -1, 1) planeKeeper[1].CFrame = CFrame.new(Vector3.new(0, 0, 0), Vector3.new(-1, 0, 0)) planeKeeper[1].Length = planeKeeper[1].Adornee.Size.X planeKeeper[2].SizeRelativeOffset = Vector3.new(1, -1, -1) planeKeeper[2].CFrame = CFrame.new(Vector3.new(0, 0, 0), Vector3.new(0, 0, 1)) planeKeeper[2].Length = planeKeeper[1].Adornee.Size.Z planeKeeper[3].SizeRelativeOffset = Vector3.new(-1, -1, 1) planeKeeper[3].CFrame = CFrame.new(Vector3.new(0, 0, 0), Vector3.new(0, 0, -1)) planeKeeper[3].Length = planeKeeper[1].Adornee.Size.Z planeKeeper[4].SizeRelativeOffset = Vector3.new(-1, -1, -1) planeKeeper[4].CFrame = CFrame.new(Vector3.new(0, 0, 0), Vector3.new(1, 0, 0)) planeKeeper[4].Length = planeKeeper[1].Adornee.Size.X for i = minZ - 4, maxZ + 4 do p0[0] = minX - 4; p0[2] = i; p1[0] = maxX + 4; p1[2] = i; local isStrongLine = i % 4 == 0; local isFarStrongLine = i % 16 == 0 or i == minZ - 4 or i == maxZ + 4; local transparency = 1.0 - (isFarStrongLine and 1.0 or (isStrongLine and farTransparency or transparencyDist)) if (p0[0] <= p1[0] and p0[1] <= p1[1] and p0[2] <= p1[2] and transparency < 1.0) then drawTemporaryLine(workplaneFrame:pointToWorldSpace(Vector3.new(p0[0], p0[1], p0[2])), workplaneFrame:pointToWorldSpace(Vector3.new(p1[0], p1[1], p1[2])), transparency, isStrongLine and 1.8 or 1.5, adornee) end end for i = minX - 4, maxX + 4 do p0[0] = i p0[2] = minZ - 4 p1[0] = i p1[2] = maxZ + 4 local isStrongLine = i % 4 == 0 local isFarStrongLine = i % 16 == 0 or i == minX - 4 or i == maxX + 4 local transparency = 1.0 - (isFarStrongLine and 1.0 or (isStrongLine and farTransparency or transparencyDist)) if (p0[0] <= p1[0] and p0[1] <= p1[1] and p0[2] <= p1[2] and transparency < 1.0) then drawTemporaryLine(workplaneFrame:pointToWorldSpace(Vector3.new(p0[0],p0[1],p0[2])), workplaneFrame:pointToWorldSpace(Vector3.new(p1[0],p1[1],p1[2])), transparency, isStrongLine and 1.8 or 1.5, adornee) end end local v1 = workplaneFrame:pointToWorldSpace(workplaneFrame:pointToObjectSpace(adornee.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1, 0, -1))) * Vector3.new(1, 0, 1)) local v2 = workplaneFrame:pointToWorldSpace(workplaneFrame:pointToObjectSpace(adornee.CFrame:pointToWorldSpace(halfSize * Vector3.new(-1, 0, 1))) * Vector3.new(1, 0, 1)) local v3 = workplaneFrame:pointToWorldSpace(workplaneFrame:pointToObjectSpace(adornee.CFrame:pointToWorldSpace(halfSize * Vector3.new(1, 0, -1))) * Vector3.new(1, 0, 1)) local v4 = workplaneFrame:pointToWorldSpace(workplaneFrame:pointToObjectSpace(adornee.CFrame:pointToWorldSpace(halfSize * Vector3.new(1, 0, 1))) * Vector3.new(1, 0, 1)) drawTemporaryLine( v1, v2, 0.0, 2.0, adornee) drawTemporaryLine( v3, v4, 0.0, 2.0, adornee) drawTemporaryLine( v1, v3, 0.0, 2.0, adornee) drawTemporaryLine( v2, v4, 0.0, 2.0, adornee) local tmpPlane = Instance.new("BoxHandleAdornment", cg) --tmp.CFrame = adornee.CFrame:toObjectSpace(cframe) -- workplaneFrame -- camera_pos local distFromPlane = workplaneFrame:pointToObjectSpace(camera_pos).y tmpPlane.Size = (adornee.Size * Vector3.new(1, 0, 1)) + Vector3.new(0, (distFromPlane / 20) * .005, 0) local objectLocation = workplaneFrame:toObjectSpace(adornee.CFrame) tmpPlane.CFrame = adornee.CFrame:toObjectSpace(workplaneFrame:toWorldSpace(objectLocation - objectLocation.p * Vector3.new(0, 1, 0))) tmpPlane.Color3 = lightBlue tmpPlane.Transparency = 0.5 tmpPlane.Adornee = adornee table.insert(tempPoints, tmpPlane) end --drawline updateMiniPlane(adornee.CFrame) end local function getAdornmentWorldCFrame(adornment) if adornment and adornment.Adornee then local adornee = adornment.Adornee if adornee:IsA("Model") and not adornee:IsA("Workspace") then adornee = Metapart.convertToPart(adornee) end local cframe = adornee.CFrame local worldSpaceCFrame = cframe * adornment.CFrame local worldSpaceOffset = cframe:pointToWorldSpace(adornment.SizeRelativeOffset * adornee.Size * 0.5) - cframe.p return worldSpaceCFrame + worldSpaceOffset; end return CFrame.new() end local function updateAdornmentPositions() if not initialized then return end if not adornKeeper or not adornKeeper[R_XY] then return end local mouseDown = Input.getButtonState(Input.Enum.Key.MOUSE_BUTTON1) clearTemporaryLines() local selection = game:GetService("Selection"):Get() if #selection <= 0 then setTranslateAdornVisibility(false) setPlaneVisibility(false) setRotateAdornVisibility(false) setScaleAdornVisibility(false) return end if not adornKeeper[R_XY][1].Adornee then return end local selectionContainsPVInstance = false for i, v in ipairs(selection) do if v:IsA("PVInstance") and not v:IsA("Workspace") and not v:IsA("Terrain") then selectionContainsPVInstance = true break end end if not selectionContainsPVInstance then setTranslateAdornVisibility(false) setPlaneVisibility(false) setRotateAdornVisibility(false) setScaleAdornVisibility(false) return end if mouseDown then if (currentHandle == R_XY or currentHandle == R_XZ or currentHandle == R_YZ) then setRotateAdornVisibility(false) setScaleAdornVisibility(false) setTranslateAdornVisibility(false) elseif (currentHandle == S_X_POS or currentHandle == S_X_NEG or currentHandle == S_Z_POS or currentHandle == S_Z_NEG or currentHandle == S_Y_POS or currentHandle == S_X_POS_Z_POS or currentHandle == S_X_POS_Z_NEG or currentHandle == S_X_NEG_Z_POS or currentHandle == S_X_NEG_Z_NEG) then setRotateAdornVisibility(false) setTranslateAdornVisibility(false) setScaleAdornVisibility(false) setAdornVisibility(currentHandle, true) elseif currentHandle == T_Y_POS then setTranslateAdornVisibility(true) setRotateAdornVisibility(false) setScaleAdornVisibility(false) elseif currentHandle == H_PLANE then setRotateAdornVisibility(false) setTranslateAdornVisibility(false) setScaleAdornVisibility(false) else --setTranslateAdornVisibility(true) --setPlaneVisibility(true) --setScaleAdornVisibility(not adornKeeper[R_XY][1].Adornee:IsA("Model") and selectionSize == 1) end elseif not planeSelectionActive then setRotateAdornVisibility(true) setTranslateAdornVisibility(true) if #selection == 1 and selection[1]:IsA("BasePart") then setScaleAdornVisibility(true) end end local adornee = adornKeeper[R_XY][1].Adornee local cameraPos = game.Workspace.CurrentCamera.CoordinateFrame.p local localPosition = adornee.CFrame:pointToObjectSpace(cameraPos).Unit local octant = localPosition / Vector3.new(math.abs(localPosition.X), math.abs(localPosition.Y), math.abs(localPosition.Z)) octant = Vector3.new(octant.X == octant.X and octant.X or 1, octant.Y == octant.Y and octant.Y or 1, octant.Z == octant.Z and octant.Z or 1) local invisLocalPos = adornKeeper[T_Y_POS][1].Adornee.CFrame:pointToObjectSpace(cameraPos).Unit local inisOctant = invisLocalPos / Vector3.new(math.abs(invisLocalPos.X), math.abs(invisLocalPos.Y), math.abs(invisLocalPos.Z)) local positionChange = math.max(adornee.CFrame:pointToObjectSpace(cameraPos).Magnitude / 15.0, 1.0) * 0.35 if not mouseDown then adornKeeper[T_Y_POS][1].SizeRelativeOffset = Vector3.new(0, inisOctant.Y, 0) adornKeeper[T_Y_POS][1].CFrame = CFrame.new(Vector3.new(0, inisOctant.Y * positionChange * 2.0 / 0.35, 0), Vector3.new(0, (inisOctant.Y * positionChange * 2.0 / 0.35) + inisOctant.Y, 0)) adornKeeper[T_Y_POS][2].SizeRelativeOffset = Vector3.new(0, inisOctant.Y, 0) adornKeeper[T_Y_POS][2].CFrame = adornKeeper[T_Y_POS][1].CFrame + Vector3.new(0, (((adornKeeper[T_Y_POS][1].Height / 2) - (adornKeeper[T_Y_POS][2].Height / 1.75)) * inisOctant.Y), 0) yScale = octant.Y adornKeeper[S_Y_POS][1].SizeRelativeOffset = Vector3.new(0, octant.Y, 0) adornKeeper[S_Y_POS][2].SizeRelativeOffset = Vector3.new(0, octant.Y, 0) shadowKeeper[S_Y_POS].SizeRelativeOffset = Vector3.new(0, octant.Y, 0) end --blue - Z Axis adornKeeper[R_XY][2].SizeRelativeOffset = Vector3.new(octant.X, octant.Y, -octant.Z) adornKeeper[R_XY][2].CFrame = CFrame.new(Vector3.new(octant.X * positionChange , octant.Y * positionChange, 0), Vector3.new(octant.X * positionChange, octant.Y * positionChange, octant.Z)) adornKeeper[R_XY][2].CFrame = adornKeeper[R_XY][2].CFrame * CFrame.Angles(0, 0, ((octant.X > 0 and math.pi/2 or 0) + (octant.Y > 0 and 0 or math.pi/2) + (octant.Z > 0 and 0 or (octant.Y > 0 and -math.pi/2 or math.pi/2))) * (octant.X > 0 and 1 or -1)) rotateAxisLine[3].Visible = false adornKeeper[R_XY][1].Visible = false if currentHandle == R_XY then local length = adornee.Size.Z + 2 rotateAxisLine[3].Adornee = adornee rotateAxisLine[3].CFrame = CFrame.new(Vector3.new(0, 0, length * 0.5))-- * CFrame.Angles(math.rad(90),0,0) rotateAxisLine[3].Length = length rotateAxisLine[3].Visible = true if not mouseDown then adornKeeper[R_XY][1].SizeRelativeOffset = adornKeeper[R_XY][2].SizeRelativeOffset adornKeeper[R_XY][1].CFrame = adornKeeper[R_XY][2].CFrame adornKeeper[R_XY][1].Visible = true adornKeeper[R_XY][2].CFrame = adornKeeper[R_XY][2].CFrame + Vector3.new(0, 0, octant.Z * .075 * positionChange) end end --green - Y Axis adornKeeper[R_XZ][2].SizeRelativeOffset = Vector3.new(octant.X, -octant.Y, octant.Z) adornKeeper[R_XZ][2].CFrame = CFrame.new(Vector3.new(octant.X * (positionChange + .2), 0, octant.Z * (positionChange + .2)), Vector3.new(octant.X * (positionChange + .2), octant.Y, octant.Z * (positionChange + .2))) adornKeeper[R_XZ][2].CFrame = adornKeeper[R_XZ][2].CFrame * CFrame.Angles(0, 0, (((octant.X > 0 and math.pi/2 or 0) + (octant.Z > 0 and math.pi/2 or 0) + (octant.Y > 0 and 0 or (octant.Z > 0 and -math.pi/2 or math.pi/2))) * (octant.X > 0 and 1 or -1) - (math.pi/2))) rotateAxisLine[2].Visible = false adornKeeper[R_XZ][1].Visible = false if currentHandle == R_XZ then local length = adornee.Size.Y + 2 rotateAxisLine[2].Adornee = adornee rotateAxisLine[2].CFrame = CFrame.new(Vector3.new(0, -length * 0.5, 0)) * CFrame.Angles(math.rad(90),0,0) rotateAxisLine[2].Length = length rotateAxisLine[2].Visible = true if not mouseDown then adornKeeper[R_XZ][1].SizeRelativeOffset = adornKeeper[R_XZ][2].SizeRelativeOffset adornKeeper[R_XZ][1].CFrame = adornKeeper[R_XZ][2].CFrame adornKeeper[R_XZ][1].Visible = true adornKeeper[R_XZ][2].CFrame = adornKeeper[R_XZ][2].CFrame + Vector3.new(0, octant.Y * .075 * positionChange, 0) end end --red - X Axis adornKeeper[R_YZ][2].SizeRelativeOffset = Vector3.new(-octant.X, octant.Y, octant.Z) adornKeeper[R_YZ][2].CFrame = CFrame.new(Vector3.new(0, octant.Y * positionChange, octant.Z * positionChange), Vector3.new(octant.X, octant.Y * positionChange, octant.Z * positionChange)) adornKeeper[R_YZ][2].CFrame = adornKeeper[R_YZ][2].CFrame * CFrame.Angles(0, 0, ((octant.Z > 0 and math.pi/2 or 0) + (octant.Y > 0 and 0 or math.pi/2) + (octant.X > 0 and (octant.Y > 0 and -math.pi/2 or math.pi/2) or 0)) * (octant.Z > 0 and 1 or -1)) rotateAxisLine[1].Visible = false adornKeeper[R_YZ][1].Visible = false if currentHandle == R_YZ then local length = adornee.Size.X + 2 rotateAxisLine[1].Adornee = adornee rotateAxisLine[1].CFrame = CFrame.new(Vector3.new(length * 0.5, 0, 0)) * CFrame.Angles(0,math.rad(90),0) rotateAxisLine[1].Length = length rotateAxisLine[1].Visible = true if not mouseDown then adornKeeper[R_YZ][1].SizeRelativeOffset = adornKeeper[R_YZ][2].SizeRelativeOffset adornKeeper[R_YZ][1].CFrame = adornKeeper[R_YZ][2].CFrame adornKeeper[R_YZ][1].Visible = true adornKeeper[R_YZ][2].CFrame = adornKeeper[R_YZ][2].CFrame + Vector3.new(octant.X * 0.075 * positionChange, 0, 0) end end --resize adorns for k,v in pairs(adornKeeper) do if k == H_PLANE then else local location = getAdornmentWorldCFrame(v[2]).p if v[1]:IsA("BoxHandleAdornment") then local sizeIncrease = math.max((location - cameraPos).Magnitude / 10.0, 1.0) v[1].Size = v[6] * sizeIncrease v[2].Size = v[7] * sizeIncrease if shadowKeeper[k] then shadowKeeper[k].Size = v[6] * sizeIncrease end local direction = (getAdornmentWorldCFrame(v[1]).p - cameraPos) local ray = Ray.new(cameraPos, direction) local part, location = game.Workspace:FindPartOnRay(ray) if (location - cameraPos).magnitude >= direction.magnitude - .2 then v[1].AlwaysOnTop = true v[2].AlwaysOnTop = true else v[1].AlwaysOnTop = false v[2].AlwaysOnTop = false end elseif v[1]:IsA("ConeHandleAdornment") then local sizeIncrease = math.max((location - cameraPos).Magnitude / 15.0, 1.0) v[1].Height = v[6].X * sizeIncrease v[1].Radius = v[6].Y * sizeIncrease v[2].Height = v[7].X * sizeIncrease v[2].Radius = v[7].Y * sizeIncrease elseif v[1]:IsA("ImageHandleAdornment") then --local sizeIncrease = math.max(((location - cameraPos).Magnitude / 40.0) + 1, 1.0) local sizeIncrease = math.max(((location - cameraPos).Magnitude / 30.0) + 1, 1.0) v[1].Size = v[6] * sizeIncrease v[2].Size = v[7] * sizeIncrease end --always on top end end local transition = nil if planeHoverTransition or planeSelectionActive then if currentPlaneHoverTransitionLevel < 1 then currentPlaneHoverTransitionLevel = math.min(currentPlaneHoverTransitionLevel + planeHoverTransitionSpeed, 1) transition = Utility.smoothstep(0, 1, currentPlaneHoverTransitionLevel) end else if currentPlaneHoverTransitionLevel > 0 then currentPlaneHoverTransitionLevel = math.max(currentPlaneHoverTransitionLevel - planeHoverTransitionSpeed, 0) transition = Utility.smoothstep(0, 1, currentPlaneHoverTransitionLevel) end end if transition then local colorEnter = Utility.colorMultiply(adornKeeper[H_PLANE][4][1], transition) local colorLeave = Utility.colorMultiply(adornKeeper[H_PLANE][5][1], 1-transition) adornKeeper[H_PLANE][2].ImageColor3 = Utility.colorAdd(colorEnter, colorLeave) adornKeeper[H_PLANE][2].ImageTransparency = (transition * adornKeeper[H_PLANE][4][2]) + ((1-transition) * adornKeeper[H_PLANE][5][2]) for i = 1, 8 do miniPlaneLines[i].ImageColor3 = Utility.colorAdd(Utility.colorMultiply(black, transition), Utility.colorMultiply(white, 1-transition)) end end if planeSelectionActive then adornKeeper[H_PLANE][2].ImageColor3 = rbxBlue adornKeeper[H_PLANE][2].ImageTransparency = 0 end -- end --set planeColor --setAlways on top --local adornee = Metapart.convertToPart(adornKeeper[R_XY][1].Adornee) --local cameraPos = Input.getMouseLocation() -- local halfSize = adornee.Size * 0.5 -- local localCameraPos = adornee.CFrame:pointToObjectSpace(cameraPos) -- if (localCameraPos.X > -halfSize.X) and (localCameraPos.Y > -halfSize.Y) then -- adornKeeper[S_X_NEG][1].AlwaysOnTop = false -- adornKeeper[S_X_NEG][2].AlwaysOnTop = false -- else -- adornKeeper[S_X_NEG][1].AlwaysOnTop = true -- adornKeeper[S_X_NEG][2].AlwaysOnTop = true -- end updatePlanePosition() end local function getCurrentAdornment() return adornKeeper[currentHandle] end local function setWorkplaneAccessor(func) workplaneAccessor = func end local function getCurrentHandle() return currentHandle end local function setCurrentHandle(value) currentHandle = value end local function scaleOne(point1, point2, direction) setLinePosition1(point1, point2, direction) local mag = (point1 - point2).magnitude setUnitText1(string.format("%.2f",mag), TYPE_STUDS) setSizeLineVisible1(true) local midpoint = ((point1 + point2) / 2) + direction setTextVisible1(true, game.Workspace.Camera:WorldToScreenPoint(midpoint)) end local function scaleTwo(point1, point2, direction) setLinePosition2(point1, point2, direction) local mag = (point1 - point2).magnitude setUnitText2(string.format("%.2f",mag), TYPE_STUDS) setSizeLineVisible2(true) local midpoint = ((point1 + point2) / 2) + direction setTextVisible2(true, game.Workspace.Camera:WorldToScreenPoint(midpoint)) end local function showRotate(cframe, radius, degrees, textLocation) internalRotate = (cframe.p - textLocation).magnitude < radius if not internalRotate then degrees = math.floor(degrees) end if degrees > 180 then degrees = degrees - 360 elseif degrees < -180 then degrees = degrees + 360 end setRotatePosition(cframe, radius, degrees) setRotateVisible(true) setUnitText1(string.format(internalRotate and "%.1f" or "%d", -degrees), TYPE_DEGREES) --text Location is currently at plane intersection point, not actual text location --local realLocation = cframe.p + ((textLocation - cframe.p).unit * radius) local realLocation = (cframe * CFrame.Angles(0, 0, math.rad(degrees + 180))):toWorldSpace(CFrame.new(Vector3.new(radius, 0, 0))).p setTextVisible1(true, game.Workspace.Camera:WorldToScreenPoint(realLocation)) end local function drawPlaneCenter(cframe) setPlaneFrame(cframe) setPlaneCFrameVisible(true) end local function clearExtraAdorns() setTextVisible1(false) setTextVisible2(false) setSizeLineVisible1(false) setSizeLineVisible2(false) setRotateVisible(false) setPlaneCFrameVisible(false) clearRotation() renderRotation = false end local function setAllAdornVisibility(value) setRotateAdornVisibility(value) setTranslateAdornVisibility(value) setScaleAdornVisibility(value) end local function resetShadow(handle) if shadowKeeper[handle] then shadowKeeper[handle].Color3 = Color3.new(0, 0, 0) shadowKeeper[handle].Transparency = shadowTransparency end end local function isPlaneSelectingModeOn() return planeSelectionActive end local function setPlaneSelectingMode(value) planeSelectionActive = value planeHoverTransition = value end local function destroyAdorns() initialized = false for i, v in pairs(adornKeeper) do v[1]:Destroy() if v[2] then v[2]:Destroy() end adornKeeper[i] = nil end for i, v in pairs(shadowKeeper) do v:Destroy() shadowKeeper[i] = nil end for i, v in pairs(planeKeeper) do v:Destroy() adornKeeper[i] = nil end for i, v in ipairs(miniPlaneLines) do miniPlaneLines[i]:Destroy() miniPlaneLines[i] = nil end for i, v in ipairs(rotateLines) do rotateLines[i]:Destroy() rotateLines[i] = nil end clearTemporaryLines() strayAdornee:Destroy() strayAdornee = nil end local function grabHandle() end local function releaseHandle() --called on mouse release, releases the current handle clearExtraAdorns() if not currentlyOverHandle and currentHandle ~= H_NONE then if currentHandle == H_PLANE then planeHoverTransition = false else adornKeeper[currentHandle][2].Color3 = adornKeeper[currentHandle][5][1] adornKeeper[currentHandle][2].Transparency = adornKeeper[currentHandle][5][2] resetShadow(currentHandle) end currentHandle = H_NONE for k, v in pairs(hoveredHandles) do currentHandle = k break end end if currentHandle ~= H_PLANE then setPlaneSelectingMode(false) end end local function getYScale() return yScale end local function isOverPlaneSelect() if not hoveredHandles[H_PLANE] then return false end -- We're already sure -- Need to check mouse position too, as MouseLeave doesn't like us all the time -- (Removing the mouse (quickly) from the button doesn't fire MouseLeave) local label = adornKeeper[H_PLANE][1] local s = label.AbsolutePosition - label.AbsoluteSize/2 local e = label.AbsolutePosition + label.AbsoluteSize/2 local mouse = Input.getMouse() local x,y = mouse.X, mouse.Y if x < s.X or x > e.X then return false end return y > s.Y and y < e.Y end local function resetDragger() for i=1,13 do resetHandle(i) end releaseHandle() setAllAdornVisibility(false) hoveredHandles = {} currentHandle = H_NONE currentlyOverHandle = false planeSelectionActive = false end local module = {} module.initializeAdorns = initializeAdorns module.destroyAdorns = destroyAdorns module.adornInstanceWithTranslate = adornInstanceWithTranslate module.adornInstanceWithScale = adornInstanceWithScale module.adornInstanceWithRotate = adornInstanceWithRotate module.adornInstanceWithPlane = adornInstanceWithPlane module.updateAdornmentPositions = updateAdornmentPositions module.setPlaneVisibility = setPlaneVisibility module.setAllAdornVisibility = setAllAdornVisibility module.setRotateAdornVisibility = setRotateAdornVisibility module.setTranslateAdornVisibility = setTranslateAdornVisibility module.setScaleAdornVisibility = setScaleAdornVisibility module.getCurrentAdornment = getCurrentAdornment module.scaleOne = scaleOne module.scaleTwo = scaleTwo module.showRotate = showRotate module.clearExtraAdorns = clearExtraAdorns module.hoverLeaveHandle = hoverLeaveHandle module.drawPlaneCenter = drawPlaneCenter module.setWorkplaneAccessor = setWorkplaneAccessor module.resetShadow = resetShadow module.getCurrentHandle = getCurrentHandle module.setCurrentHandle = setCurrentHandle module.isPlaneSelectingModeOn = isPlaneSelectingModeOn module.setPlaneSelectingMode = setPlaneSelectingMode module.getAdornmentWorldCFrame = getAdornmentWorldCFrame module.grabHandle = grabHandle module.releaseHandle = releaseHandle module.isOverPlaneSelect = isOverPlaneSelect module.getYScale = getYScale module.resetDragger = resetDragger return module ]]> Input Rubberband math.min(startPos.X, endPos.X) and projection.Y < math.max(startPos.Y, endPos.Y) and projection.Y > math.min(startPos.Y, endPos.Y) then table.insert(ret, v) end end end end return ret end local function isRubberBandDragInProgress() return rubberBandDragInProgress end function startRubberbandDrag(location) rubberBandDragInProgress = true Adorn.setPlaneVisibility(false) selectionBoxStart = location partsInRubberbandSelection = {} if not selectionScreenGui then selectionScreenGui = Instance.new("ScreenGui", game:GetService("CoreGui")) rubberBandFrames[1] = Instance.new("Frame", selectionScreenGui) rubberBandFrames[1].BorderSizePixel = 0 rubberBandFrames[1].BackgroundColor3 = Color3.new(0,0,0) rubberBandFrames[2] = Instance.new("Frame", selectionScreenGui) rubberBandFrames[2].BorderSizePixel = 0 rubberBandFrames[2].BackgroundColor3 = Color3.new(0,0,0) rubberBandFrames[3] = Instance.new("Frame", selectionScreenGui) rubberBandFrames[3].BorderSizePixel = 0 rubberBandFrames[3].BackgroundColor3 = Color3.new(0,0,0) rubberBandFrames[4] = Instance.new("Frame", selectionScreenGui) rubberBandFrames[4].BorderSizePixel = 0 rubberBandFrames[4].BackgroundColor3 = Color3.new(0,0,0) end end function finishRubberbandDrag() if not rubberBandDragInProgress then return end rubberBandDragInProgress = false game:GetService("Selection"):Set(partsInRubberbandSelection) selectionBoxStart = nil if selectionScreenGui then selectionScreenGui:Destroy() selectionScreenGui = nil end end function updateRubberBand(location) if not rubberBandDragInProgress then return end --local initialTick = tick() local xSize = location.X - selectionBoxStart.X local ySize = location.Y - selectionBoxStart.Y rubberBandFrames[1].Size = UDim2.new(0, rubberbandBorderSize, 0, ySize + rubberbandBorderSize) rubberBandFrames[2].Size = UDim2.new(0, rubberbandBorderSize, 0, ySize + rubberbandBorderSize) rubberBandFrames[3].Size = UDim2.new(0, xSize + rubberbandBorderSize, 0, rubberbandBorderSize) rubberBandFrames[4].Size = UDim2.new(0, xSize + rubberbandBorderSize, 0, rubberbandBorderSize) rubberBandFrames[1].Position = UDim2.new(0, selectionBoxStart.X, 0, selectionBoxStart.Y) rubberBandFrames[2].Position = UDim2.new(0, selectionBoxStart.X + xSize, 0, selectionBoxStart.Y) rubberBandFrames[3].Position = UDim2.new(0, selectionBoxStart.X, 0, selectionBoxStart.Y) rubberBandFrames[4].Position = UDim2.new(0, selectionBoxStart.X, 0, selectionBoxStart.Y + ySize) partsInRubberbandSelection = findPartsInSelection(selectionBoxStart, location) if (#game:GetService("Selection"):Get() ~= #partsInRubberbandSelection) then game:GetService("Selection"):Set(partsInRubberbandSelection) --spawn(function() game:GetService("Selection"):Set(partsInRubberbandSelection) end) end end local module = {} module.isRubberBandDragInProgress = isRubberBandDragInProgress module.startRubberbandDrag = startRubberbandDrag module.finishRubberbandDrag = finishRubberbandDrag module.updateRubberBand = updateRubberBand return module ]]>