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