forked from aya/aya
6783 lines
206 KiB
Plaintext
6783 lines
206 KiB
Plaintext
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="4">
|
|
<External>null</External>
|
|
<External>nil</External>
|
|
<Item class="Model" referent="RBXFF48B6F5FC3A49258275900CE593573C">
|
|
<Properties>
|
|
<CoordinateFrame name="ModelInPrimary">
|
|
<X>0</X>
|
|
<Y>0</Y>
|
|
<Z>0</Z>
|
|
<R00>1</R00>
|
|
<R01>0</R01>
|
|
<R02>0</R02>
|
|
<R10>0</R10>
|
|
<R11>1</R11>
|
|
<R12>0</R12>
|
|
<R20>0</R20>
|
|
<R21>0</R21>
|
|
<R22>1</R22>
|
|
</CoordinateFrame>
|
|
<string name="Name">PrecisionDragger</string>
|
|
<Ref name="PrimaryPart">null</Ref>
|
|
</Properties>
|
|
<Item class="Script" referent="RBX32238E2E0B5D45398607F92063AEAE64">
|
|
<Properties>
|
|
<bool name="Disabled">false</bool>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Precision</string>
|
|
<ProtectedString name="Source"><![CDATA[local plugin, settings = plugin, settings
|
|
|
|
-----------------------------------
|
|
-----------MODULE SCRIPTS----------
|
|
-----------------------------------
|
|
|
|
local Collision = require(script.Parent.Collision)
|
|
local Utility = require(script.Parent.Utility)
|
|
local List = require(script.Parent.List)
|
|
local Selection = require(script.Parent.Selection)
|
|
local Metapart = require(script.Parent.Metapart)
|
|
local FuzzyMath = require(script.Parent.FuzzyMath)
|
|
local Round = require(script.Parent.Round)
|
|
local Extent = require(script.Parent.Extent)
|
|
local Adorn = require(script.Parent.Adornments)
|
|
local Input = require(script.Parent.Input)
|
|
local RubberBand = require(script.Parent.Rubberband)
|
|
|
|
-----------------------------------
|
|
--------------VARIABLES------------
|
|
-----------------------------------
|
|
|
|
local loaded = false
|
|
local on = false
|
|
|
|
local toolbar = plugin:CreateToolbar("Transform")
|
|
local toolbarbutton = toolbar:CreateButton("Transform", "Precision Dragger", "")
|
|
|
|
local mouse = plugin:GetMouse(true)
|
|
Input.setMouse(mouse)
|
|
|
|
local allowYSnap = false
|
|
|
|
local initializedDragPlane = false
|
|
|
|
local adornmentUpdateNeeded = true
|
|
|
|
local cg = game:GetService("CoreGui")
|
|
local uis = game:GetService("UserInputService")
|
|
|
|
local shouldBreakJoints = true
|
|
|
|
local currentlySelecting = false
|
|
|
|
local planeObject = nil
|
|
local holoBox = nil
|
|
local planeDragging = false
|
|
|
|
local localSpace = true
|
|
|
|
local rotateAdornPart = nil
|
|
|
|
local hoveredHandles = {n = 0}
|
|
|
|
local currentDist = nil
|
|
|
|
local originalSize = nil
|
|
local originalCFrame = nil
|
|
|
|
local lastDist = Vector3.new(0,0,0)
|
|
|
|
local originalPosition = nil;
|
|
|
|
local freeDragging = nil;
|
|
|
|
local startLocation = nil
|
|
|
|
local baseDragPlane = nil
|
|
local dragPlane = nil
|
|
|
|
local secondaryPart = nil
|
|
local secondaryLocation = nil
|
|
local secondaryPartCFrame = nil
|
|
|
|
local secondaryPartSideSelected = nil
|
|
local workplaneOffset = 0
|
|
|
|
local invisiblePart = nil
|
|
local previousAABBCFrame = nil
|
|
|
|
local selectionBoxStart = nil
|
|
|
|
local pV0, pV1, pV2 = nil
|
|
local w0, w1, w2, w3 = nil
|
|
|
|
local workplaneFrame = CFrame.new()
|
|
|
|
local currentlyOverHandle = false
|
|
local initialPos = nil
|
|
|
|
local mouseOffsetOnGrabHandle = nil
|
|
|
|
------------------------------------
|
|
|
|
local castPart = nil
|
|
local castSecondaryPart = nil
|
|
local castSecondaryLocation = nil
|
|
local castSecondaryPartSideSelected = nil
|
|
local castPartAdorn = nil
|
|
local castPlane = nil
|
|
local castWorkplaneOffset = nil
|
|
|
|
local waypointUndoConnection = nil
|
|
local waypointRedoConnection = nil
|
|
local inputBeganConnection = nil
|
|
local inputEndedConnection = nil
|
|
local inputChangedConnection = nil
|
|
local selectionChangedConnection = nil
|
|
local renderSteppedConnection = nil
|
|
local dragEnterConnection = nil
|
|
|
|
local selectedPartCFrameBeforeDrag = nil
|
|
|
|
local dragFromToolbox = false
|
|
|
|
local clickOnUpdate = false
|
|
|
|
local updateInvisiblePartNeeded = 0
|
|
|
|
local handleWasDragged = false
|
|
|
|
local dragPart
|
|
local dragPartHoloBox
|
|
|
|
local TOP_SIDE = 0
|
|
local BOTTOM_SIDE = 1
|
|
local LEFT_SIDE = 2
|
|
local RIGHT_SIDE = 3
|
|
local FRONT_SIDE = 4
|
|
local BACK_SIDE = 5
|
|
|
|
local function getWorkplane()
|
|
return workplaneFrame, workplaneOffset
|
|
end
|
|
|
|
Adorn.setWorkplaneAccessor(getWorkplane)
|
|
|
|
game:GetService("ContentProvider"):Preload("roblox.com/asset?id=232196030")
|
|
game:GetService("ContentProvider"):Preload("roblox.com/asset?id=233257118")
|
|
|
|
--handleDeclaration
|
|
local H_NONE = 0
|
|
local T_Y_POS = 1
|
|
local S_X_POS = 2
|
|
local S_X_NEG = 3
|
|
local S_Z_POS = 4
|
|
local S_Z_NEG = 5
|
|
local S_Y_POS = 6
|
|
local S_X_POS_Z_POS = 7
|
|
local S_X_POS_Z_NEG = 8
|
|
local S_X_NEG_Z_POS = 9
|
|
local S_X_NEG_Z_NEG = 10
|
|
local R_XY = 11
|
|
local R_XZ = 12
|
|
local R_YZ = 13
|
|
local H_PLANE = 14
|
|
|
|
local handlesCurrentlyOver = {}
|
|
|
|
local setAnchoredStateForMovingParts = false
|
|
local states = {}
|
|
|
|
|
|
local function BreakJoints(part)
|
|
if part:IsA("Wrapped") then
|
|
workspace:UnjoinFromOutsiders({part.Object})
|
|
elseif part:IsA("BasePart") then
|
|
workspace:UnjoinFromOutsiders({part})
|
|
end
|
|
end
|
|
|
|
local function JoinSelection()
|
|
local selection = Selection.getFilteredSelection()
|
|
workspace:JoinToOutsiders(selection, plugin:GetJoinMode())
|
|
end
|
|
|
|
local function UnjoinSelection()
|
|
local selection = Selection.getFilteredSelection()
|
|
workspace:UnjoinFromOutsiders(selection)
|
|
end
|
|
|
|
--duplicate code, please consolidate
|
|
local roots = {}
|
|
|
|
local function getAllRoots(item)
|
|
|
|
if item:IsA("BasePart") and item:GetRootPart() then
|
|
roots[item:GetRootPart()] = true
|
|
end
|
|
|
|
local children = item:GetChildren()
|
|
for i, v in ipairs(children) do
|
|
getAllRoots(v)
|
|
end
|
|
end
|
|
|
|
local function getSelectionRoots()
|
|
local selection = Selection.getFilteredSelection()
|
|
|
|
roots = {}
|
|
|
|
for i, v in ipairs(selection) do
|
|
getAllRoots(v)
|
|
end
|
|
return roots
|
|
end
|
|
|
|
local function setModelCFrame(model, finalCF)
|
|
roots = {}
|
|
getAllRoots(model)
|
|
|
|
local originalCF = model:GetModelCFrame()
|
|
|
|
for k, v in pairs(roots) do
|
|
k.CFrame = finalCF:toWorldSpace(originalCF:toObjectSpace(k.CFrame))
|
|
end
|
|
end
|
|
|
|
local function moveSelection(initialCFrame, finalCFrame)
|
|
local rootSelection = getSelectionRoots()
|
|
|
|
for k, v in pairs(rootSelection) do
|
|
k.CFrame = finalCFrame:toWorldSpace(initialCFrame:toObjectSpace(k.CFrame))
|
|
end
|
|
end
|
|
|
|
local function updateRotatePart()
|
|
|
|
local selection = Selection.getFilteredSelection()
|
|
|
|
if #selection > 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)
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXD4E2E5155CD5450E9385590707C45D37">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Collision</string>
|
|
<ProtectedString name="Source"><![CDATA[local Utility = require(script.Parent.Utility)
|
|
local List = require(script.Parent.List)
|
|
|
|
local maxMove = 1000
|
|
|
|
local function movePrimitivesDelta(part, direction, movedSoFar, isMetapart)
|
|
if isMetapart then
|
|
part.TranslateFromTo(part.CFrame, part.CFrame + direction)
|
|
else
|
|
part.CFrame = part.CFrame + direction
|
|
end
|
|
|
|
return movedSoFar + direction
|
|
end
|
|
|
|
local function searchIn(part, ignoreList, direction, moveBy, inExtent, outExtent)
|
|
if math.abs(moveBy) < 0.0001 then return end
|
|
|
|
while not List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) do
|
|
outExtent = part.CFrame.p
|
|
if (part.CFrame.p + (direction.Unit * moveBy) == inExtent) then
|
|
|
|
searchIn(part, ignoreList, direction, moveBy * 0.5, inExtent, outExtent)
|
|
return
|
|
end
|
|
movePrimitivesDelta(part, direction.Unit * moveBy, Vector3.new(0,0,0))
|
|
end
|
|
|
|
searchOut(part, ignoreList, direction, moveBy * -0.5, inExtent, outExtent)
|
|
end
|
|
|
|
function searchOut(part, ignoreList, direction, moveBy, inExtent, outExtent)
|
|
|
|
if math.abs(moveBy) < 0.0001 then
|
|
part.CFrame = part.CFrame - part.CFrame.p + outExtent
|
|
return
|
|
end
|
|
|
|
while List.itemsHasItemNotInList(part:GetTouchingParts(), ignoreList) do
|
|
inExtent = part.CFrame.p
|
|
if outExtent and (part.CFrame.p + (direction.Unit * moveBy) == outExtent) then
|
|
searchOut(part, ignoreList, direction, moveBy * 0.5, inExtent, outExtent)
|
|
return
|
|
end
|
|
movePrimitivesDelta(part, direction.Unit * moveBy, Vector3.new(0,0,0))
|
|
end
|
|
|
|
searchIn(part, ignoreList, direction, moveBy * -0.5, inExtent, outExtent)
|
|
end
|
|
|
|
local function castOut(part, ray)
|
|
local oriOS = part.CFrame:pointToObjectSpace(ray.Origin)
|
|
local dirOS = part.CFrame:pointToObjectSpace(ray.Origin + ray.Direction) - oriOS
|
|
dirOS = dirOS.Unit
|
|
|
|
local hSize = part.Size / 2
|
|
local normDir = dirOS / Vector3.new(math.abs(dirOS.X), math.abs(dirOS.Y), math.abs(dirOS.Z))
|
|
|
|
local iPoints = normDir * hSize
|
|
|
|
if ((iPoints * 2) - oriOS).Magnitude < oriOS.Magnitude then return nil end
|
|
--assume inside box
|
|
|
|
local yI = ((iPoints.X - oriOS.X) / dirOS.X * dirOS.Y) + oriOS.Y
|
|
local zI = ((iPoints.X - oriOS.X) / dirOS.X * dirOS.Z) + oriOS.Z
|
|
local xPlaneI = Vector3.new(iPoints.X, yI, zI)
|
|
|
|
local xI = ((iPoints.Y - oriOS.Y) / dirOS.Y * dirOS.X) + oriOS.X
|
|
zI = ((iPoints.Y - oriOS.Y) / dirOS.Y * dirOS.Z) + oriOS.Z
|
|
local yPlaneI = Vector3.new(xI, iPoints.Y, zI)
|
|
|
|
xI = ((iPoints.Z - oriOS.Z) / dirOS.Z * dirOS.X) + oriOS.X
|
|
yI = ((iPoints.Z - oriOS.Z) / dirOS.Z * dirOS.Y) + oriOS.Y
|
|
local zPlaneI = Vector3.new(xI, yI, iPoints.Z)
|
|
|
|
local xPlaneDist = (xPlaneI.Y <= hSize.Y and xPlaneI.Y >= -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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXFF5624FEC0F442A598EFF5AF78462673">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Utility</string>
|
|
<ProtectedString name="Source"><![CDATA[function min(...)
|
|
local arg = {...}
|
|
local currentMin = nil
|
|
|
|
for i, v in ipairs(arg) do
|
|
|
|
if not currentMin then
|
|
currentMin = v
|
|
elseif v then
|
|
currentMin = v < currentMin and v or currentMin
|
|
end
|
|
end
|
|
return currentMin
|
|
end
|
|
|
|
function max(...)
|
|
local arg = {...}
|
|
local currentMax = nil
|
|
|
|
for i,v in ipairs(arg) do
|
|
if not currentMax then
|
|
currentMax = v
|
|
elseif v then
|
|
currentMax = v > 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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX7388EEFDA62E4B88BFB0E645FBA57CEF">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">List</string>
|
|
<ProtectedString name="Source"><![CDATA[local function itemExistsInList(item, list)
|
|
for i = 1, #list do
|
|
if list[i] == item then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function filterOutItems(items, list)
|
|
if not items then return list end
|
|
|
|
local ret = {}
|
|
for i = 1, #list do
|
|
if not itemExistsInList(list[i], items) then
|
|
table.insert(ret, list[i])
|
|
end
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
local function listContainsAnyItems(items, list)
|
|
local map = {}
|
|
for i = 1, #list do
|
|
map[list[i]] = true
|
|
end
|
|
|
|
for i = 1, #items do
|
|
if map[items[i]] then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function itemsHasItemNotInList(items, list)
|
|
if not list and items then return true end
|
|
if not list and not items then return false end
|
|
|
|
local map = {}
|
|
for i = 1, #list do
|
|
map[list[i]] = true
|
|
end
|
|
|
|
for i = 1, #items do
|
|
if not map[items[i]] then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function combineLists(t1, t2)
|
|
for i, v in ipairs(t2) do
|
|
table.insert(t1, v)
|
|
end
|
|
return t1
|
|
end
|
|
|
|
local function createIgnoreListGivenWhiteList(root, whiteList)
|
|
local children = root:GetChildren()
|
|
local ignoreList = {}
|
|
for i, v in ipairs(children) do
|
|
if v:IsA("PartInstance") and not itemExistsInList(v, whiteList) then table.insert(ignoreList, v) end
|
|
if v:IsA("PVInstance") and not v:IsA("Workspace") then
|
|
ignoreList = combineLists(ignoreList, createIgnoreListGivenWhiteList(v, whiteList))
|
|
end
|
|
end
|
|
return ignoreList
|
|
end
|
|
|
|
local function removeDuplicates(t)
|
|
local final = {}
|
|
local set = {}
|
|
|
|
for i = 1, #t do
|
|
if not set[t[i]] then
|
|
set[t[i]] = true
|
|
table.insert(final, t[i])
|
|
end
|
|
end
|
|
return final
|
|
end
|
|
|
|
local function listDoesNotContainType(list, type)
|
|
for i = 1, #list do
|
|
if list[i]:IsA(type) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--------------------------------------------------------------
|
|
--------------------------------------------------------------
|
|
--------------------------------------------------------------
|
|
|
|
local module = {}
|
|
|
|
module.itemExistsInList = itemExistsInList
|
|
module.filterOutItems = filterOutItems
|
|
module.combineLists = combineLists
|
|
module.createIgnoreListGivenWhiteList = createIgnoreListGivenWhiteList
|
|
module.removeDuplicates = removeDuplicates
|
|
module.listDoesNotContainType = listDoesNotContainType
|
|
module.listContainsAnyItems = listContainsAnyItems
|
|
module.itemsHasItemNotInList = itemsHasItemNotInList
|
|
|
|
return module
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXCE34A64BF4EF44DAA3CA0B5534AB1E42">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Plane</string>
|
|
<ProtectedString name="Source"><![CDATA[
|
|
|
|
|
|
|
|
local function new(position, normal, thirdPoint)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local module = {}
|
|
|
|
module.new = new
|
|
|
|
return module
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXC1F19C1ED8FB454AA5F7CC77F8577911">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Selection</string>
|
|
<ProtectedString name="Source"><![CDATA[local RubberBand = require(script.Parent.Rubberband)
|
|
local Metapart = require(script.Parent.Metapart)
|
|
|
|
local currentSelection = {} --selection with everything
|
|
local filteredSelection = {} -- filtered selection
|
|
|
|
local filteredSelectionMetaPart = nil
|
|
|
|
local function calculateFilteredSelection()
|
|
local filteredSelection = {}
|
|
local currentIndex = 1
|
|
|
|
for i,v in ipairs(currentSelection) do
|
|
if (v:IsA("BasePart") or v:IsA("Model")) and not v:IsA("Workspace") and not v:IsA("Terrain") and v:isDescendantOf(workspace) then
|
|
filteredSelection[currentIndex] = v
|
|
currentIndex = currentIndex + 1
|
|
end
|
|
end
|
|
|
|
return filteredSelection
|
|
end
|
|
|
|
|
|
local function updateSelection()
|
|
currentSelection = game:GetService("Selection"):Get()
|
|
|
|
if RubberBand.isRubberBandDragInProgress() then return end
|
|
|
|
filteredSelection = calculateFilteredSelection()
|
|
if filteredSelectionMetaPart then
|
|
filteredSelectionMetaPart.Unsubscribe()
|
|
end
|
|
filteredSelectionMetaPart = Metapart.convertToPart(filteredSelection, true)
|
|
end
|
|
|
|
local function getCurrentSelection()
|
|
return currentSelection
|
|
end
|
|
|
|
local function getFilteredSelection()
|
|
return filteredSelection
|
|
end
|
|
|
|
local function getFilteredSelectionMetapart()
|
|
return filteredSelectionMetaPart
|
|
end
|
|
|
|
|
|
|
|
local module = {}
|
|
|
|
module.updateSelection = updateSelection
|
|
|
|
module.getCurrentSelection = getCurrentSelection
|
|
module.getFilteredSelection = getFilteredSelection
|
|
module.getFilteredSelectionMetapart = getFilteredSelectionMetapart
|
|
|
|
return module]]></ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX878086A5BAB84AC48FF6166E14D97F56">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Metapart</string>
|
|
<ProtectedString name="Source"><![CDATA[local FuzzyMath = require(script.Parent.FuzzyMath)
|
|
local List = require(script.Parent.List)
|
|
local Extent = require(script.Parent.Extent)
|
|
|
|
local collisionSizeLimit = 10
|
|
local originalCFrames = {}
|
|
|
|
local function itemHasChanged(item)
|
|
return item.CFrame ~= originalCFrames[item]
|
|
end
|
|
|
|
local roots = {}
|
|
|
|
local function getAllRoots(item)
|
|
if item:IsA("BasePart") then
|
|
local itemRoot = item:GetRootPart()
|
|
if itemRoot then
|
|
roots[itemRoot] = true
|
|
end
|
|
end
|
|
|
|
local children = item:GetChildren()
|
|
for i, v in ipairs(children) do
|
|
getAllRoots(v)
|
|
end
|
|
end
|
|
|
|
local function getAllRootsFromTable(t)
|
|
roots = {}
|
|
for i = 1, #t do
|
|
getAllRoots(t[i])
|
|
end
|
|
|
|
local finalRoots = {}
|
|
|
|
for k, v in pairs(roots) do
|
|
table.insert(finalRoots, k)
|
|
end
|
|
return finalRoots
|
|
end
|
|
|
|
local function setModelCFrame(model, finalCF)
|
|
roots = {}
|
|
getAllRoots(model)
|
|
|
|
local originalCF = model:GetModelCFrame()
|
|
|
|
for k, v in pairs(roots) do
|
|
k.CFrame = finalCF:toWorldSpace(originalCF:toObjectSpace(k.CFrame))
|
|
end
|
|
end
|
|
|
|
local function getAllChildren(item, items)
|
|
if not items then items = {} end
|
|
|
|
if item:IsA("BasePart") then
|
|
table.insert(items, item)
|
|
end
|
|
|
|
local children = item:GetChildren()
|
|
for i = 1, #children do
|
|
items = getAllChildren(children[i], items)
|
|
end
|
|
|
|
return items
|
|
end
|
|
|
|
local function getAllChildrenFromTable(t)
|
|
local items = {}
|
|
for i = 1, #t do
|
|
items = getAllChildren(t[i], items)
|
|
end
|
|
|
|
return items
|
|
end
|
|
|
|
function forcePrimaryPart(model)
|
|
if not model.PrimaryPart then
|
|
local possiblePrimaryPart = nil
|
|
local children = model:GetChildren()
|
|
for i = 1, #children do
|
|
if children[i]:IsA("BasePart") then
|
|
possiblePrimaryPart = children[i]
|
|
|
|
local location = possiblePrimaryPart.CFrame.p
|
|
|
|
if FuzzyMath.visiblyIdentityCFrame(possiblePrimaryPart.CFrame, 0.00001) then
|
|
break
|
|
end
|
|
elseif children[i]:IsA("Model") and not children[i]:IsA("Workspace") then
|
|
|
|
local part = children[i]
|
|
forcePrimaryPart(children[i])
|
|
|
|
if part.PrimaryPart then
|
|
possiblePrimaryPart = part.PrimaryPart
|
|
if FuzzyMath.visiblyIdentityCFrame(possiblePrimaryPart.CFrame, 0.00001) then
|
|
break
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
if possiblePrimaryPart then
|
|
model.PrimaryPart = possiblePrimaryPart
|
|
end
|
|
end
|
|
end
|
|
|
|
local sanitizationPrecision = 1000000
|
|
|
|
local function sanitizeFloat(value)
|
|
return value > 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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX3D4844446CDF4548B040031F20C7114B">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">FuzzyMath</string>
|
|
<ProtectedString name="Source"><![CDATA[local Round = require(script.Parent.Round)
|
|
|
|
function fuzzyCompare(value1, value2, delta)
|
|
if not delta then
|
|
delta = 0.0001
|
|
end
|
|
|
|
if (value2 >= 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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXE52F582989124525815DB72705E39426">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Round</string>
|
|
<ProtectedString name="Source"><![CDATA[function round(value)
|
|
return math.floor(value + 0.5)
|
|
end
|
|
|
|
function roundToNearest(value, interval)
|
|
if not interval or interval == 0 then return value end
|
|
|
|
local tmp = value + (interval / 2)
|
|
return tmp - (tmp % interval)
|
|
end
|
|
|
|
function roundVector3(value)
|
|
return Vector3.new(round(value.X), round(value.Y), round(value.Z))
|
|
end
|
|
|
|
function roundVector3ToNearest(value, interval)
|
|
return Vector3.new( roundToNearest(value.X, interval),
|
|
roundToNearest(value.Y, interval),
|
|
roundToNearest(value.Z, interval))
|
|
end
|
|
|
|
local module = {}
|
|
|
|
module.roundToNearest = roundToNearest
|
|
module.roundVector3ToNearest = roundVector3ToNearest
|
|
|
|
|
|
return module
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX4814AA6E2A8C498EA9FBE2592E9F9E3E">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Extent</string>
|
|
<ProtectedString name="Source"><![CDATA[local Utility = require(script.Parent.Utility)
|
|
--local Selection = require(script.Parent.Selection)
|
|
|
|
function unionVector3(vect, lowerV, upperV, extentSpace)
|
|
|
|
if not vect then
|
|
return lowerV, upperV
|
|
end
|
|
|
|
vect = extentSpace:pointToObjectSpace(vect)
|
|
if not lowerV then
|
|
lowerV = vect
|
|
else
|
|
lowerV = extentSpace:pointToObjectSpace(lowerV)
|
|
end
|
|
|
|
if not upperV then
|
|
upperV = vect
|
|
else
|
|
upperV = extentSpace:pointToObjectSpace(upperV)
|
|
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
|
|
|
|
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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBXDB76DCC650044E09890A9A0966AD32AB">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Adornments</string>
|
|
<ProtectedString name="Source"><![CDATA[local Metapart = require(script.Parent.Metapart)
|
|
local Input = require(script.Parent.Input)
|
|
local Utility = require(script.Parent.Utility)
|
|
|
|
local adornKeeper = {}
|
|
local shadowKeeper = {}
|
|
local planeKeeper = {}
|
|
|
|
local cg = game:GetService("CoreGui")
|
|
|
|
local initialized = false
|
|
|
|
local workplaneAccessor = nil
|
|
|
|
local strayAdornee = nil
|
|
|
|
--measure
|
|
local measureFrame1 = nil
|
|
local measureLabel1 = nil
|
|
local unitLabel1 = nil
|
|
|
|
local measureFrame2 = nil
|
|
local measureLabel2 = nil
|
|
local unitLabel2 = nil
|
|
|
|
local measureLine1 = nil
|
|
local measureLine2 = nil
|
|
local measureLine3 = nil
|
|
local measureCone1 = nil
|
|
local measureCone2 = nil
|
|
|
|
local measureLine4 = nil
|
|
local measureLine5 = nil
|
|
local measureLine6 = nil
|
|
local measureCone3 = nil
|
|
local measureCone4 = nil
|
|
|
|
local planeOriginLines = {}
|
|
|
|
local rotateLines = {}
|
|
|
|
local rotateAxisLine = {}
|
|
|
|
local renderRotation = false
|
|
local internalRotate = false
|
|
|
|
local planeHoverTransition = false
|
|
local currentPlaneHoverTransitionLevel = 0
|
|
local planeHoverTransitionSpeed = 0.1
|
|
|
|
local planeSelectionActive = false
|
|
|
|
local miniPlaneLines = {}
|
|
|
|
local shadowTransparency = 0.7
|
|
local shadowTransparencyHover = 0.2
|
|
--local gridSphere = nil
|
|
|
|
local oneDegrees = "ayaasset://textures/transformOneDegree.png"
|
|
local fiveDegrees = "ayaasset://textures/transformFiveDegrees.png"
|
|
local oneEigthDegrees = "ayaasset://textures/transformTwentyTwoDegrees.png"
|
|
local ninetyDegrees = "ayaasset://textures/transformNinetyDegrees.png"
|
|
local planeImage = "ayaasset://textures/whiteCircle.png"
|
|
local rotationArrow = "ayaasset://textures/rotationArrow.png"
|
|
local gradient = "ayaasset://textures/gradient.png"
|
|
--local planeImage = "ayaasset://textures/planeImage.png"
|
|
|
|
--duplicate - please consolidate
|
|
local H_NONE = 0
|
|
local T_Y_POS = 1
|
|
local S_X_POS = 2
|
|
local S_X_NEG = 3
|
|
local S_Z_POS = 4
|
|
local S_Z_NEG = 5
|
|
local S_Y_POS = 6
|
|
local S_X_POS_Z_POS = 7
|
|
local S_X_POS_Z_NEG = 8
|
|
local S_X_NEG_Z_POS = 9
|
|
local S_X_NEG_Z_NEG = 10
|
|
local R_XY = 11 -- 3
|
|
local R_XZ = 12 -- 2
|
|
local R_YZ = 13 -- 1
|
|
local H_PLANE = 14
|
|
|
|
local currentHandle = H_NONE
|
|
|
|
local hoveredHandles = {}
|
|
|
|
local yScale = 1
|
|
|
|
local TYPE_STUDS = 1
|
|
local TYPE_DEGREES = 2
|
|
|
|
local function setUnitText1(str, type)
|
|
if not measureFrame1 then return end
|
|
|
|
measureLabel1.Text = str
|
|
unitLabel1.Text = type == TYPE_STUDS and "studs" or "o"
|
|
|
|
measureLabel1.Size = UDim2.new(0, measureLabel1.TextBounds.X, 0, measureLabel1.TextBounds.Y)
|
|
measureLabel1.Position = UDim2.new(0, 3, 0, 0)
|
|
unitLabel1.Size = UDim2.new(0, unitLabel1.TextBounds.X, 0, unitLabel1.TextBounds.Y)
|
|
|
|
unitLabel1.Position = UDim2.new(0, measureLabel1.TextBounds.X + 4, 0, type == TYPE_STUDS and 7 or 0)
|
|
|
|
measureFrame1.Size = UDim2.new(0, measureLabel1.TextBounds.X + unitLabel1.TextBounds.X + 7, 0, measureLabel1.TextBounds.Y + 2)
|
|
end
|
|
|
|
local function setUnitText2(str, type)
|
|
if not measureFrame2 then return end
|
|
|
|
measureLabel2.Text = str
|
|
unitLabel2.Text = type == TYPE_STUDS and "studs" or "o"
|
|
|
|
measureLabel2.Size = UDim2.new(0, measureLabel2.TextBounds.X, 0, measureLabel2.TextBounds.Y)
|
|
measureLabel2.Position = UDim2.new(0, 3, 0, 0)
|
|
unitLabel2.Size = UDim2.new(0, unitLabel2.TextBounds.X, 0, unitLabel2.TextBounds.Y)
|
|
|
|
unitLabel2.Position = UDim2.new(0, measureLabel2.TextBounds.X + 4, 0, type == TYPE_STUDS and 7 or 5)
|
|
|
|
measureFrame2.Size = UDim2.new(0, measureLabel2.TextBounds.X + unitLabel2.TextBounds.X + 7, 0, measureLabel2.TextBounds.Y + 2)
|
|
end
|
|
|
|
local function setLinePosition1(point1, point2, direction)
|
|
measureCone1.CFrame = CFrame.new(point2, point1) - point2 + point1 + ((point2 - point1).unit * measureCone1.Height) + direction
|
|
measureCone2.CFrame = CFrame.new(point1, point2) - point1 + point2 - ((point2 - point1).unit * measureCone2.Height) + direction
|
|
measureLine1.CFrame = CFrame.new(point1 + direction, point2 + direction)
|
|
measureLine1.Length = (point1 - point2).magnitude
|
|
|
|
measureLine2.CFrame = CFrame.new(point1, point1 + direction)
|
|
measureLine2.Length = direction.magnitude
|
|
measureLine3.CFrame = CFrame.new(point2, point2 + direction)
|
|
measureLine3.Length = direction.magnitude
|
|
end
|
|
|
|
local function setLinePosition2(point1, point2, direction)
|
|
measureCone3.CFrame = CFrame.new(point2, point1) - point2 + point1 + ((point2 - point1).unit * measureCone3.Height) + direction
|
|
measureCone4.CFrame = CFrame.new(point1, point2) - point1 + point2 - ((point2 - point1).unit * measureCone4.Height) + direction
|
|
measureLine4.CFrame = CFrame.new(point1 + direction, point2 + direction)
|
|
measureLine4.Length = (point1 - point2).magnitude
|
|
|
|
measureLine5.CFrame = CFrame.new(point1, point1 + direction)
|
|
measureLine5.Length = direction.magnitude
|
|
measureLine6.CFrame = CFrame.new(point2, point2 + direction)
|
|
measureLine6.Length = direction.magnitude
|
|
end
|
|
|
|
local tmpRotation = {}
|
|
|
|
local function clearRotation()
|
|
for k, v in pairs(tmpRotation) do
|
|
v:Destroy()
|
|
tmpRotation[k] = nil
|
|
end
|
|
end
|
|
|
|
local function setRotation(cframe, radius, degrees)
|
|
clearRotation()
|
|
|
|
if degrees == 0 then return end
|
|
|
|
local direction = degrees / math.abs(degrees)
|
|
|
|
local flip = cframe:pointToObjectSpace(game.Workspace.Camera.CoordinateFrame.p).Z > 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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX37EE36A0E09B455A9461F14E76C51AE1">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Input</string>
|
|
<ProtectedString name="Source"><![CDATA[
|
|
--buttons
|
|
local enum =
|
|
{
|
|
Key = {
|
|
MOUSE_BUTTON1 = 0,
|
|
MOUSE_BUTTON2 = 1
|
|
},
|
|
State = {
|
|
UP = nil,
|
|
DOWN = true
|
|
}
|
|
}
|
|
|
|
--current Mouse state
|
|
local mouseState = {}
|
|
local mouse = nil
|
|
|
|
local connected = false
|
|
|
|
local function getMouse() return mouse end
|
|
local function setMouse(value)
|
|
mouse = value
|
|
end
|
|
|
|
local function getButtonState(button)
|
|
return mouseState[button]
|
|
end
|
|
|
|
local function setButtonState(button, state)
|
|
mouseState[button] = state
|
|
end
|
|
|
|
local function getMouseLocation()
|
|
if not mouse then return nil end
|
|
return mouse.Origin.p
|
|
end
|
|
|
|
local module = {}
|
|
|
|
module.setMouse = setMouse
|
|
module.getMouse = getMouse
|
|
|
|
module.Enum = enum
|
|
|
|
module.getButtonState = getButtonState
|
|
module.setButtonState = setButtonState
|
|
module.getMouseLocation = getMouseLocation
|
|
|
|
return module
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
<Item class="ModuleScript" referent="RBX65E143CBEB744869B17274F381CA0B5B">
|
|
<Properties>
|
|
<Content name="LinkedSource">
|
|
<null></null>
|
|
</Content>
|
|
<string name="Name">Rubberband</string>
|
|
<ProtectedString name="Source"><![CDATA[local Adorn = require(script.Parent.Adornments)
|
|
|
|
local selectionScreenGui = nil
|
|
local rubberBandFrames = {}
|
|
|
|
local rubberbandBorderSize = 2
|
|
|
|
local selectionSettingInProgress = false
|
|
local partsInRubberbandSelection = {}
|
|
|
|
local rubberBandDragInProgress = false
|
|
|
|
local function findFirstCFrame(parent)
|
|
if parent:IsA("BasePart") then return parent.CFrame end
|
|
local children = parent:GetChildren()
|
|
for i = 1, #children do
|
|
local cframe = findFirstCFrame(children[i])
|
|
if cframe then return cframe end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function findPartsInSelection(startPos, endPos)
|
|
|
|
local children = game.Workspace:GetChildren()
|
|
local camera = workspace.CurrentCamera
|
|
|
|
local ret = {}
|
|
|
|
for i, v in ipairs(children) do
|
|
local pos = nil
|
|
local isLocked = false
|
|
|
|
if v:IsA("BasePart") then
|
|
pos = v.CFrame.p
|
|
isLocked = v.Locked
|
|
elseif v:IsA("Model") and not v:IsA("Workspace") then
|
|
if v.PrimaryPart then
|
|
pos = v:GetPrimaryPartCFrame().p
|
|
else
|
|
pos = v:GetModelCFrame().p
|
|
end
|
|
isLocked = false
|
|
elseif v:IsA("Tool") then
|
|
pos = findFirstCFrame(v)
|
|
isLocked = false
|
|
end
|
|
|
|
if pos then
|
|
if camera.CoordinateFrame:pointToObjectSpace(pos).Z < 0 then -- check to make sure the object is in front of the camera
|
|
local projection = camera:WorldToScreenPoint(pos)
|
|
|
|
if not isLocked and
|
|
projection.X < math.max(startPos.X, endPos.X) and
|
|
projection.X > 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
|
|
]]> </ProtectedString>
|
|
</Properties>
|
|
</Item>
|
|
</Item>
|
|
</roblox> |