diff --git a/Core/init.lua b/Core/init.lua index 25a2763..effaceb 100644 --- a/Core/init.lua +++ b/Core/init.lua @@ -26,6 +26,7 @@ Make = require(Tool.Libraries.Make) local Roact = require(Tool.Vendor:WaitForChild 'Roact') local Maid = require(Tool.Libraries:WaitForChild 'Maid') local Cryo = require(Tool.Libraries:WaitForChild('Cryo')) +local JointUtils = require(Tool.Libraries:WaitForChild("JointUtils")) -- References Support.ImportServices(); @@ -960,84 +961,8 @@ function ToggleSwitch(CurrentButtonName, SwitchContainer) end; end; --- References to reduce indexing time -local GetConnectedParts = Instance.new('Part').GetConnectedParts; -local GetChildren = script.GetChildren; - -function GetPartJoints(Part, Whitelist) - -- Returns any manual joints involving `Part` - - local Joints = {}; - - -- Get joints stored inside `Part` - for Joint, JointParent in pairs(SearchJoints(Part, Part, Whitelist)) do - Joints[Joint] = JointParent; - end; - - -- Get joints stored inside connected parts - for _, ConnectedPart in pairs(GetConnectedParts(Part)) do - for Joint, JointParent in pairs(SearchJoints(ConnectedPart, Part, Whitelist)) do - Joints[Joint] = JointParent; - end; - end; - - -- Return all found joints - return Joints; - -end; - --- Types of joints to assume should be preserved -local ManualJointTypes = Support.FlipTable { 'Weld', 'ManualWeld', 'ManualGlue', 'Motor', 'Motor6D' }; - -function SearchJoints(Haystack, Part, Whitelist) - -- Searches for and returns manual joints in `Haystack` involving `Part` and other parts in `Whitelist` - - local Joints = {}; - - -- Search the haystack for joints involving `Part` - for _, Item in pairs(GetChildren(Haystack)) do - - -- Check if this item is a manual, intentional joint - if ManualJointTypes[Item.ClassName] and - (Whitelist[Item.Part0] and Whitelist[Item.Part1]) then - - -- Save joint and state if intentional - Joints[Item] = Item.Parent; - - end; - - end; - - -- Return the found joints - return Joints; - -end; - -function RestoreJoints(Joints) - -- Restores the joints from the given `Joints` data - - -- Restore each joint - for Joint, JointParent in pairs(Joints) do - Joint.Parent = JointParent; - end; - -end; - -function PreserveJoints(Part, Whitelist) - -- Preserves and returns intentional joints of `Part` connecting parts in `Whitelist` - - -- Get the part's joints - local Joints = GetPartJoints(Part, Whitelist); - - -- Save the joints from being broken - for Joint in pairs(Joints) do - Joint.Parent = nil; - end; - - -- Return the joints - return Joints; - -end; +Core.PreserveJoints = JointUtils.PreserveJoints +Core.RestoreJoints = JointUtils.RestoreJoints -- Initialize the UI InitializeUI(); diff --git a/Libraries/JointUtils.lua b/Libraries/JointUtils.lua new file mode 100644 index 0000000..a10340b --- /dev/null +++ b/Libraries/JointUtils.lua @@ -0,0 +1,72 @@ +--!strict + +type DisabledJointSnapshot = { + disabledJoints: {JointInstance}, + initialPart0CFrame: {[JointInstance]: CFrame}, + initialPart1CFrame: {[JointInstance]: CFrame}, + anchoredParts: {BasePart}, +} + +local JointUtils = {} + +function JointUtils.RestoreJoints(jointSnapshot: DisabledJointSnapshot) + for _, joint in jointSnapshot.disabledJoints do + if not (joint.Part0 and joint.Part1) then + continue + end + + local initialOffset = jointSnapshot.initialPart0CFrame[joint]:ToObjectSpace(jointSnapshot.initialPart1CFrame[joint]) + local newOffset = joint.Part0.CFrame:ToObjectSpace(joint.Part1.CFrame) + local didJointChange = (initialOffset ~= newOffset) + if didJointChange then + local didPart0Change = joint.Part0.CFrame ~= jointSnapshot.initialPart0CFrame[joint] + local didPart1Change = joint.Part1.CFrame ~= jointSnapshot.initialPart1CFrame[joint] + if didPart0Change then + joint.C0 = joint.Part0.CFrame:ToObjectSpace(joint.Part1.CFrame:ToWorldSpace(joint.C1)) + end + if didPart1Change then + joint.C1 = joint.Part1.CFrame:ToObjectSpace(joint.Part0.CFrame:ToWorldSpace(joint.C0)) + end + end + joint.Enabled = true + end + for _, part in jointSnapshot.anchoredParts do + part.Anchored = false + end +end + +function JointUtils.PreserveJoints(part: BasePart): DisabledJointSnapshot + local initialPart0CFrame: {[JointInstance]: CFrame} = {} + local initialPart1CFrame: {[JointInstance]: CFrame} = {} + local disabledJoints: {JointInstance} = {} + local anchoredParts: {BasePart} = {} + + for _, joint in part:GetJoints() do + if not (joint:IsA("JointInstance") and joint.Enabled and joint.Part0 and joint.Part1) then + continue + end + + joint.Enabled = false + table.insert(disabledJoints, joint) + initialPart0CFrame[joint] = joint.Part0.CFrame + initialPart1CFrame[joint] = joint.Part1.CFrame + + if not joint.Part0.Anchored then + table.insert(anchoredParts, joint.Part0) + joint.Part0.Anchored = true + end + if not joint.Part1.Anchored then + table.insert(anchoredParts, joint.Part1) + joint.Part1.Anchored = true + end + end + + return { + disabledJoints = disabledJoints, + initialPart0CFrame = initialPart0CFrame, + initialPart1CFrame = initialPart1CFrame, + anchoredParts = anchoredParts, + } +end + +return JointUtils \ No newline at end of file diff --git a/SyncAPI.lua b/SyncAPI.lua index 4db97e9..7955a65 100644 --- a/SyncAPI.lua +++ b/SyncAPI.lua @@ -11,6 +11,7 @@ Security = require(Tool.Core.Security); RegionModule = require(Tool.Libraries.Region); Support = require(Tool.Libraries.SupportLibrary); Serialization = require(Tool.Libraries.SerializationV3); +local JointUtils = require(Tool.Libraries.JointUtils) -- Import services Support.ImportServices(); @@ -459,7 +460,7 @@ Actions = { -- Preserve joints for Part, Change in pairs(PartChangeSet) do - Change.Joints = PreserveJoints(Part, PartChangeSet) + Change.Joints = JointUtils.PreserveJoints(Part) end; -- Perform each change @@ -467,7 +468,6 @@ Actions = { -- Stabilize the parts and maintain the original anchor state Part.Anchored = true; - Part:BreakJoints(); Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); @@ -491,8 +491,8 @@ Actions = { -- Restore the parts' original states for Part, Change in pairs(PartChangeSet) do - Part:MakeJoints(); - RestoreJoints(Change.Joints); + JointUtils.RestoreJoints(Change.Joints) + Part:MakeJoints() Part.Anchored = Change.InitialState.Anchored; end; @@ -531,12 +531,15 @@ Actions = { end; end; + for Part, Change in pairs(ChangeSet) do + Change.Joints = JointUtils.PreserveJoints(Part) + end + -- Perform each change for Part, Change in pairs(ChangeSet) do -- Stabilize the parts and maintain the original anchor state Part.Anchored = true; - Part:BreakJoints(); Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); @@ -559,6 +562,7 @@ Actions = { -- Restore the parts' original states for Part, Change in pairs(ChangeSet) do + JointUtils.RestoreJoints(Change.Joints) Part:MakeJoints(); Part.Anchored = Change.InitialState.Anchored; end; @@ -609,7 +613,7 @@ Actions = { -- Preserve joints for Part, Change in pairs(PartChangeSet) do - Change.Joints = PreserveJoints(Part, PartChangeSet) + Change.Joints = JointUtils.PreserveJoints(Part) end; -- Perform each change @@ -617,7 +621,6 @@ Actions = { -- Stabilize the parts and maintain the original anchor state Part.Anchored = true; - Part:BreakJoints(); Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); @@ -641,8 +644,8 @@ Actions = { -- Restore the parts' original states for Part, Change in pairs(PartChangeSet) do - Part:MakeJoints(); - RestoreJoints(Change.Joints); + JointUtils.RestoreJoints(Change.Joints) + Part:MakeJoints() Part.Anchored = Change.InitialState.Anchored; end; @@ -1759,84 +1762,6 @@ function GetPartsFromSelection(Selection) return Parts end --- References to reduce indexing time -local GetConnectedParts = Instance.new('Part').GetConnectedParts; -local GetChildren = script.GetChildren; - -function GetPartJoints(Part, Whitelist) - -- Returns any manual joints involving `Part` - - local Joints = {}; - - -- Get joints stored inside `Part` - for Joint, JointParent in pairs(SearchJoints(Part, Part, Whitelist)) do - Joints[Joint] = JointParent; - end; - - -- Get joints stored inside connected parts - for _, ConnectedPart in pairs(GetConnectedParts(Part)) do - for Joint, JointParent in pairs(SearchJoints(ConnectedPart, Part, Whitelist)) do - Joints[Joint] = JointParent; - end; - end; - - -- Return all found joints - return Joints; - -end; - --- Types of joints to assume should be preserved -local ManualJointTypes = Support.FlipTable { 'Weld', 'ManualWeld', 'ManualGlue', 'Motor', 'Motor6D' }; - -function SearchJoints(Haystack, Part, Whitelist) - -- Searches for and returns manual joints in `Haystack` involving `Part` and other parts in `Whitelist` - - local Joints = {}; - - -- Search the haystack for joints involving `Part` - for _, Item in pairs(GetChildren(Haystack)) do - - -- Check if this item is a manual, intentional joint - if ManualJointTypes[Item.ClassName] and - (Whitelist[Item.Part0] and Whitelist[Item.Part1]) then - - -- Save joint and state if intentional - Joints[Item] = Item.Parent; - - end; - - end; - - -- Return the found joints - return Joints; - -end; - -function RestoreJoints(Joints) - -- Restores the joints from the given `Joints` data - - -- Restore each joint - for Joint, JointParent in pairs(Joints) do - Joint.Parent = JointParent; - end; - -end; - -function PreserveJoints(Part, Whitelist) - -- Preserves and returns intentional joints of `Part` connecting parts in `Whitelist` - - -- Get the part's joints - local Joints = GetPartJoints(Part, Whitelist); - - -- Save the joints from being broken - for Joint in pairs(Joints) do - Joint.Parent = nil; - end; - - -- Return the joints - return Joints; - -end; function CreatePart(PartType) -- Creates and returns new part based on `PartType` with sensible defaults diff --git a/Tools/Move/FreeDragging.lua b/Tools/Move/FreeDragging.lua index dbee6db..cb0b235 100644 --- a/Tools/Move/FreeDragging.lua +++ b/Tools/Move/FreeDragging.lua @@ -14,6 +14,7 @@ local BoundingBox = require(Tool.Core.BoundingBox) local Libraries = Tool:WaitForChild 'Libraries' local Support = require(Libraries:WaitForChild 'SupportLibrary') local MoveUtil = require(script.Parent:WaitForChild 'Util') +local JointUtils = require(Libraries:WaitForChild("JointUtils")) -- Create class local FreeDragging = {} @@ -515,8 +516,8 @@ function FreeDragging:FinishDragging() -- Restore the original state of each part for Part, State in pairs(self.InitialPartStates) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints() - Core.RestoreJoints(State.Joints) Part.CanCollide = State.CanCollide Part.Anchored = State.Anchored end diff --git a/Tools/Move/HandleDragging.lua b/Tools/Move/HandleDragging.lua index f346d4a..97c1177 100644 --- a/Tools/Move/HandleDragging.lua +++ b/Tools/Move/HandleDragging.lua @@ -9,6 +9,7 @@ local BoundingBox = require(Tool.Core.BoundingBox) -- Libraries local Libraries = Tool:WaitForChild 'Libraries' local MoveUtil = require(script.Parent:WaitForChild 'Util') +local JointUtils = require(Libraries:WaitForChild("JointUtils")) -- Create class local HandleDragging = {} @@ -140,8 +141,8 @@ function HandleDragging:AttachHandles(Part, Autofocus) -- Make joints, restore original anchor and collision states for Part, State in pairs(self.InitialPartStates) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints() - Core.RestoreJoints(State.Joints) Part.CanCollide = State.CanCollide Part.Anchored = State.Anchored end diff --git a/Tools/Move/init.lua b/Tools/Move/init.lua index 67d84a8..36f3343 100644 --- a/Tools/Move/init.lua +++ b/Tools/Move/init.lua @@ -11,6 +11,7 @@ local UserInputService = game:GetService 'UserInputService' local Libraries = Tool:WaitForChild 'Libraries' local Signal = require(Libraries:WaitForChild 'Signal') local Maid = require(Libraries:WaitForChild 'Maid') +local JointUtils = require(Libraries:WaitForChild("JointUtils")) -- Import relevant references local Selection = Core.Selection @@ -344,8 +345,8 @@ function MoveTool:SetAxisPosition(Axis, Position) -- Restore the parts' original states for Part, State in pairs(InitialPartStates) do - Part:MakeJoints(); - Core.RestoreJoints(State.Joints); + JointUtils.RestoreJoints(State.Joints) + Part:MakeJoints() Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; end; @@ -391,8 +392,8 @@ function MoveTool:NudgeSelectionByFace(Face) -- Restore the parts' original states for Part, State in pairs(InitialPartStates) do - Part:MakeJoints(); - Core.RestoreJoints(State.Joints); + JointUtils.RestoreJoints(State.Joints) + Part:MakeJoints() Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; end; @@ -531,8 +532,7 @@ function MoveTool:PrepareSelectionForDragging() } Part.Anchored = true; Part.CanCollide = false; - InitialPartStates[Part].Joints = Core.PreserveJoints(Part, PartIndex) - Part:BreakJoints(); + InitialPartStates[Part].Joints = JointUtils.PreserveJoints(Part) Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); end; diff --git a/Tools/Resize.lua b/Tools/Resize.lua index 37c4977..39da581 100644 --- a/Tools/Resize.lua +++ b/Tools/Resize.lua @@ -11,6 +11,7 @@ local Libraries = Tool:WaitForChild 'Libraries' local Signal = require(Libraries:WaitForChild 'Signal') local Make = require(Libraries:WaitForChild 'Make') local ListenForManualWindowTrigger = require(Tool.Core:WaitForChild('ListenForManualWindowTrigger')) +local JointUtils = require(Libraries:WaitForChild("JointUtils")) -- Import relevant references Selection = Core.Selection; @@ -363,6 +364,7 @@ function ShowHandles() -- Make joints, restore original anchor and collision states for Part, State in pairs(InitialState) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints(); Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; @@ -621,6 +623,7 @@ function SetAxisSize(Axis, Size) -- Restore the parts' original states for Part, State in pairs(InitialStates) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints(); Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; @@ -675,6 +678,7 @@ function NudgeSelectionByFace(Face) -- Restore the parts' original states for Part, State in pairs(InitialState) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints(); Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; @@ -775,7 +779,7 @@ function PreparePartsForResizing() InitialState[Part] = { Anchored = Part.Anchored, CanCollide = Part.CanCollide, Size = Part.Size, CFrame = Part.CFrame }; Part.Anchored = true; Part.CanCollide = false; - Part:BreakJoints(); + InitialState[Part].Joints = JointUtils.PreserveJoints(Part) Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); end; @@ -1038,6 +1042,7 @@ function FinishSnapping() -- Restore the selection's original state if stage was reached if SnappingStartSelectionState then for Part, State in pairs(SnappingStartSelectionState) do + JointUtils.RestoreJoints(State.Joints) Part:MakeJoints(); Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; diff --git a/Tools/Rotate.lua b/Tools/Rotate.lua index 328c73b..67f76bd 100644 --- a/Tools/Rotate.lua +++ b/Tools/Rotate.lua @@ -12,6 +12,7 @@ local UserInputService = game:GetService('UserInputService') local Libraries = Tool:WaitForChild 'Libraries' local Make = require(Libraries:WaitForChild 'Make') local ListenForManualWindowTrigger = require(Tool.Core:WaitForChild('ListenForManualWindowTrigger')) +local JointUtils = require(Libraries:WaitForChild("JointUtils")) -- Import relevant references Selection = Core.Selection; @@ -403,8 +404,8 @@ function AttachHandles(Part, Autofocus) -- Make joints, restore original anchor and collision states for Part, State in pairs(InitialPartStates) do - Part:MakeJoints(); - Core.RestoreJoints(State.Joints); + JointUtils.RestoreJoints(State.Joints) + Part:MakeJoints() Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; end; @@ -723,8 +724,8 @@ function SetAxisAngle(Axis, Angle) -- Restore the parts' original states for Part, State in pairs(InitialPartStates) do - Part:MakeJoints(); - Core.RestoreJoints(State.Joints); + JointUtils.RestoreJoints(State.Joints) + Part:MakeJoints() Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; end; @@ -797,8 +798,8 @@ function NudgeSelectionByAxis(Axis, Direction) -- Make joints, restore original anchor and collision states for Part, State in pairs(InitialPartStates) do - Part:MakeJoints(); - Core.RestoreJoints(State.Joints); + JointUtils.RestoreJoints(State.Joints) + Part:MakeJoints() Part.CanCollide = State.CanCollide; Part.Anchored = State.Anchored; end; @@ -937,8 +938,7 @@ function PrepareSelectionForRotating() } Part.Anchored = true; Part.CanCollide = false; - InitialPartStates[Part].Joints = Core.PreserveJoints(Part, PartIndex); - Part:BreakJoints(); + InitialPartStates[Part].Joints = JointUtils.PreserveJoints(Part) Part.Velocity = Vector3.new(); Part.RotVelocity = Vector3.new(); end;