<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" 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>
			<BinaryString name="Tags"></BinaryString>
		</Properties>
		<Item class="Script" referent="RBX32238E2E0B5D45398607F92063AEAE64">
			<Properties>
				<bool name="Disabled">false</bool>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Precision</string>
				<string name="ScriptGuid">{c6df4bd3-735b-4b27-818b-bf999d205dfe}</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)
local FFlag = require(script.Parent.FFlag)

-----------------------------------
--------------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("rbxassetid://232196030")
game:GetService("ContentProvider"):Preload("rbxassetid://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 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 == 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_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 getShapeRenderSize(shape, size)
	if (shape == Enum.PartType.Cylinder) then
		local minYZ = math.min(size.Y, size.Z)
		return Vector3.new(size.X, minYZ, minYZ)
	end
	
	return size
end

function updateChildAttachments(part, initialSize, finalSize)
	if (not part:IsA("BasePart")) then return end
	
	local children = part:GetChildren()
	
	local shape = part.Shape
	
	initialSize = getShapeRenderSize(shape, initialSize)
	finalSize = getShapeRenderSize(shape, finalSize)
	
	for i = 1, #children do
		local child = children[i]
		if (child:IsA("Attachment")) then
			child.Position = (child.Position / initialSize) * finalSize
		end
	end
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
		
		selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle)
		selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) * 0.5)))
		
		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
	
		selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle)
		selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(((originalSize - selectedPart.Size) * 0.5)))
				
		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
		
		selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle)
		selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 0, 1)))
		
		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
		
		selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle)			
		selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(-1, 0, -1)))
		
		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

		selectedPart.Size = snapVector3ByHandle(originalSize + currentDist, currentHandle)
		selectedPart.CFrame = originalCFrame:toWorldSpace(CFrame.new(-((originalSize - selectedPart.Size) / 2) * Vector3.new(1, 0, -1)))
		
		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.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 (selectedPart) then
		updateChildAttachments(selectedPart, preActionSize, selectedPart.Size)
	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.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.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.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.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.TopSurface = Enum.SurfaceType.Smooth
		invisiblePart.BottomSurface = Enum.SurfaceType.Smooth
		invisiblePart.Archivable = false
		invisiblePart.Transparency = 1
	end
	
	if not castPart then
		castPart = Instance.new("Part")
	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 getHighestModelParent(instance)
	-- If instance or any ancestor is a Model, 
	-- return the highest Model in the ancestor hierarchy.
	-- Otherwise return nil.
	if not instance then 
		return nil
	end

	if (instance == game.Workspace) then 
		return nil
	end		
	
	-- Is there a model above me?  Use that.
	local bestResult = getHighestModelParent(instance.Parent)
	if (bestResult ~= nil) then 
		return bestResult
	end

	-- Am I a Model?	
	if instance:IsA("Model") then 
		return instance
	end
	
	-- I got nothing.
	return nil
end

function getPVInstanceForPart(instance)
	-- If this instance or any ancestor is a Model, return the highest
	-- Model in the heirarchy.
	-- Otherwise return this model iff it's a non-Workspace PVInstance.
	local result = getHighestModelParent(instance)
	if (result ~= nil) then 
		return result
	end

	if instance:IsA("PVInstance") and not instance:IsA("Workspace") then
		return instance
	else
		return nil
	end
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 = getPVInstanceForPart(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
	
	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])
	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.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, processed)
	if processed then return end
	
	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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXD4E2E5155CD5450E9385590707C45D37">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Collision</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXFF5624FEC0F442A598EFF5AF78462673">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Utility</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX7388EEFDA62E4B88BFB0E645FBA57CEF">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">List</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXCE34A64BF4EF44DAA3CA0B5534AB1E42">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Plane</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[



local function new(position, normal, thirdPoint)
	
end




local module = {}

module.new = new

return module
]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXC1F19C1ED8FB454AA5F7CC77F8577911">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Selection</string>
				<string name="ScriptGuid">{9CEB1EB8-999E-42C0-8330-8705621FC33C}</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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX878086A5BAB84AC48FF6166E14D97F56">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Metapart</string>
				<string name="ScriptGuid">{ac1c6065-5b5c-476b-a9bd-b3852c9ec590}</string>
				<ProtectedString name="Source"><![CDATA[local FuzzyMath = require(script.Parent.FuzzyMath)
local List = require(script.Parent.List)
local Extent = require(script.Parent.Extent)
local FFlag = require(script.Parent.FFlag)

local collisionSizeLimit = 10
local originalCFrames = {}

local function checkIsWrapped(object)
	if (object == nil or typeof(object) ~= "table") then
		return false
	end
	
	return object.IsWrapped == true
end

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 checkIsWrapped(object) then
		return object
	end
	
	if not subscribe then subscribe = false 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.IsWrapped = true
		
		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 == "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 checkIsWrapped(object) 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.IsWrapped = true		
		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 == "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 checkIsWrapped(object) 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.IsWrapped = true
		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 == "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 checkIsWrapped(object) then
			return object
		end
		
		local finalObject = {}
		
		finalObject.object = object
		finalObject.mt = {}
		finalObject.info = {}
		finalObject.IsWrapped = true
		
		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 == "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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX3D4844446CDF4548B040031F20C7114B">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">FuzzyMath</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXE52F582989124525815DB72705E39426">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Round</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX4814AA6E2A8C498EA9FBE2592E9F9E3E">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Extent</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXDB76DCC650044E09890A9A0966AD32AB">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Adornments</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[
-----------------------------------
-----------MODULE SCRIPTS----------
-----------------------------------

local Metapart = require(script.Parent.Metapart)
local Input = require(script.Parent.Input)
local Utility = require(script.Parent.Utility)
local RecyclingBin = require(script.Parent.RecyclingBin)

-----------------------------------
--------------VARIABLES------------
-----------------------------------

local adornKeeper = {}
local shadowKeeper = {}
local planeKeeper = {}

local cg = game:GetService("CoreGui")
local tempAdorns = Instance.new("Folder",cg)
tempAdorns.Name = "TransformTempAdornments"

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 measureLine = {}
local measureCone = {}

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 oneDegrees = "rbxasset://textures/transformOneDegree.png"
local fiveDegrees = "rbxasset://textures/transformFiveDegrees.png"
local oneEigthDegrees = "rbxasset://textures/transformTwentyTwoDegrees.png"
local ninetyDegrees = "rbxasset://textures/transformNinetyDegrees.png"
local planeImage = "rbxasset://textures/whiteCircle.png"
local rotationArrow = "rbxasset://textures/rotationArrow.png"--"rbxassetid://232196030"
local gradient = "rbxasset://textures/gradient.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

-----------------------------------
--------------FUNCTIONS------------
-----------------------------------

local function setUnitText(measureFrame, measureLabel, unitLabel, str, unitType)
	if not measureFrame then return end
	
	measureLabel.Text = str
	measureLabel.Size = UDim2.new(0, measureLabel.TextBounds.X, 0, measureLabel.TextBounds.Y)
	measureLabel.Position = UDim2.new(0, 3, 0, 0)
	
	unitLabel.Text = unitType == TYPE_STUDS and "studs" or "o"
	unitLabel.Size = UDim2.new(0, unitLabel.TextBounds.X, 0, unitLabel.TextBounds.Y)
	unitLabel.Position = UDim2.new(0, measureLabel.TextBounds.X + 4, 0, unitType == TYPE_STUDS and 7 or 0)
	
	measureFrame.Size = UDim2.new(0, measureLabel.TextBounds.X + unitLabel.TextBounds.X + 7, 0, measureLabel.TextBounds.Y + 2)
end

local function setLinePosition(cone1, cone2, line1, line2, line3, point1, point2, direction)
	cone1.CFrame = CFrame.new(point2, point1) - point2 + point1 + ((point2 - point1).unit * cone1.Height) + direction
	cone2.CFrame = CFrame.new(point1, point2) - point1 + point2 - ((point2 - point1).unit * cone2.Height) + direction
	line1.CFrame = CFrame.new(point1 + direction, point2 + direction)
	line1.Length = (point1 - point2).magnitude
	
	line2.CFrame = CFrame.new(point1, point1 + direction)
	line2.Length = direction.magnitude
	line3.CFrame = CFrame.new(point2, point2 + direction)
	line3.Length = direction.magnitude
end

local rotationBin = Instance.new("Folder",tempAdorns)
rotationBin.Name = "Rotation"

local function clearRotation()
	local children = rotationBin:GetChildren()
	if #children > 0 then
		RecyclingBin:RecycleObjects(children)
	end
end

local function setRotation(cframe, radius, degrees)
	if degrees == 0 then clearRotation() 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
		
	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 pending = {}
	local function pendIt(image)
		table.insert(pending,{
			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)));
			Image = image;
		})
	end
	
		
	while amount >= 90 do
		--add 90
		if direction < 0 then
			amount = amount - 90
		end
		
		pendIt(ninetyDegrees)
		
		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
		
		pendIt(oneEigthDegrees)
		
		if direction > 0 then
			amount = amount - 22.5
		end
	end
	
	while amount >= 5 do
		if direction < 0 then
			amount = amount - 5
		end
		
		pendIt(fiveDegrees)
		
		if direction > 0 then
			amount = amount - 5
		end
	end
	
	while amount >= 1 do
		if direction < 0 then
			amount = amount - 1
		end
		
		pendIt(oneDegrees)

		if direction > 0 then
			amount = amount - 1
		end
	end
	
	local imageAdorns = RecyclingBin:Allocate("ImageHandleAdornment",#pending,rotationBin)
	for i,data in ipairs(pending) do
		local im = imageAdorns[i]
		im.Adornee = strayAdornee
		im.ZIndex = 1
		im.AlwaysOnTop = true
		im.Size = Vector2.new(radius, radius)
		im.Color3 = Color3.new(1, 0, 0)
		im.Transparency = 0.6
		im.CFrame = data.CFrame
		im.Image = data.Image
	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
		if (finalHandle == H_PLANE) then
			hoveredHandles[H_PLANE] = nil
			return
		end
		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()
		
		measureLine[1] = Instance.new("LineHandleAdornment", cg)
		measureLine[1].Visible = false
		measureLine[1].Color3 = black
		measureLine[1].Adornee = strayAdornee
		measureLine[1].AlwaysOnTop = true
		measureLine[1].ZIndex = 5
		
		measureLine[2] = measureLine[1]:Clone()
		measureLine[2].Parent = cg
		
		measureLine[3] = measureLine[1]:Clone()
		measureLine[3].Parent = cg
		
		measureLine[4] = measureLine[1]:Clone()
		measureLine[4].Parent = cg
		
		measureLine[5] = measureLine[1]:Clone()
		measureLine[5].Parent = cg
		
		measureLine[6] = measureLine[1]:Clone()
		measureLine[6].Parent = cg
		
		planeOriginLines[1] = measureLine[1]: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
				
		measureCone[1] = Instance.new("ConeHandleAdornment", cg)
		measureCone[1].Visible = false
		measureCone[1].Color3 = black
		measureCone[1].Adornee = strayAdornee
		measureCone[1].Height = 0.5
		measureCone[1].Radius = 0.1
		measureCone[1].AlwaysOnTop = true
		measureCone[1].ZIndex = 5
		
		measureCone[2] = measureCone[1]:Clone()
		measureCone[2].Parent = cg
		
		measureCone[3] = measureCone[1]:Clone()
		measureCone[3].Parent = cg
		
		measureCone[4] = measureCone[1]:Clone()
		measureCone[4].Parent = cg
		
		-- mini plane		
		miniPlaneLines[1] = Instance.new("ImageLabel", parentScreen)
		miniPlaneLines[1].Visible = false
		miniPlaneLines[1].BackgroundTransparency = 1
		miniPlaneLines[1].BorderSizePixel = 0
		miniPlaneLines[1].Image = gradient
		
		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", cg)
		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 setTextLocation(measureFrame, location)
	if location then
		measureFrame.Position = UDim2.new(0, location.X, 0, location.Y)
	end
end

local function setTextVisible(measureFrame, value, location)
	setTextLocation(measureFrame, location)	
	measureFrame.Visible = value
end

local function setSizeLineVisible1(value)
	measureLine[1].Visible = value
	measureLine[2].Visible = value
	measureLine[3].Visible = value
	measureCone[1].Visible = value
	measureCone[2].Visible = value
end

local function setSizeLineVisible2(value)
	measureLine[4].Visible = value
	measureLine[5].Visible = value
	measureLine[6].Visible = value
	measureCone[3].Visible = value
	measureCone[4].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 pendingLines = {}
local lineAlloc = Instance.new("Folder",tempAdorns)
lineAlloc.Name = "LineGrid"

local function clearTemporaryLines()
	local lines = lineAlloc:GetChildren()
	if #lines > 0 then
		RecyclingBin:RecycleObjects(lines)
	end
end

local function appendTemporaryLine(startPoint, endPoint, transparency, thickness, adornee)
	if adornee then
		local params = {startPoint, endPoint, transparency, thickness, adornee}
		table.insert(pendingLines,params)
	end
end

local function drawTemporaryLines()
	local expected = #pendingLines
	local lines = RecyclingBin:Allocate("LineHandleAdornment",expected,lineAlloc)
	for i = expected,1,-1 do
		local params = pendingLines[i]
		pendingLines[i] = nil
		
		local startPoint, endPoint, transparency, thickness, adornee = unpack(params)
		local tmp = lines[i]
		
		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
	end
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
	
	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;
	
	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
		clearTemporaryLines()
		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
			appendTemporaryLine(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
				appendTemporaryLine(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))
		appendTemporaryLine(	v1,
							v2,
							0.0, 2.0, adornee)
		appendTemporaryLine(	v3,
							v4,
							0.0, 2.0, adornee)
		appendTemporaryLine(	v1,
							v3,
							0.0, 2.0, adornee)
		appendTemporaryLine(	v2,
							v4,
							0.0, 2.0, adornee)
		
		local tmpPlane = RecyclingBin:Allocate("BoxHandleAdornment",1,lineAlloc)[1]
		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
		
		drawTemporaryLines()
	end

	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)
	
	local selection = game:GetService("Selection"):Get()
	
	if #selection <= 0 then
		setTranslateAdornVisibility(false)
		setPlaneVisibility(false)
		setRotateAdornVisibility(false)
		setScaleAdornVisibility(false)
		clearTemporaryLines()
		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)
		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 / 30.0) + 1, 1.0)
				v[1].Size = v[6] * sizeIncrease
				v[2].Size = v[7] * sizeIncrease
			end
		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

	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)
	setLinePosition(measureCone[1], measureCone[2], measureLine[1], measureLine[2], measureLine[3], point1, point2, direction)
	local mag = (point1 - point2).magnitude
	
	setUnitText(measureFrame1, measureLabel1, unitLabel1, string.format("%.2f",mag), TYPE_STUDS)
	setSizeLineVisible1(true)
	local midpoint = ((point1 + point2) / 2) + direction
	setTextVisible(measureFrame1, true, game.Workspace.Camera:WorldToScreenPoint(midpoint))
end

local function scaleTwo(point1, point2, direction)
	setLinePosition(measureCone[3], measureCone[4], measureLine[4], measureLine[5], measureLine[6], point1, point2, direction)
	local mag = (point1 - point2).magnitude
	
	setUnitText(measureFrame2, measureLabel2, unitLabel2, string.format("%.2f",mag), TYPE_STUDS)
	setSizeLineVisible2(true)
	local midpoint = ((point1 + point2) / 2) + direction
	setTextVisible(measureFrame2, 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)
	setUnitText(measureFrame1, measureLabel1, unitLabel1, 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  * CFrame.Angles(0, 0, math.rad(degrees + 180))):toWorldSpace(CFrame.new(Vector3.new(radius, 0, 0))).p
	
	setTextVisible(measureFrame1, true, game.Workspace.Camera:WorldToScreenPoint(realLocation))
end

local function drawPlaneCenter(cframe)
	setPlaneFrame(cframe)
	setPlaneCFrameVisible(true)
end

local function clearExtraAdorns()
	setTextVisible(measureFrame1, false)
	setTextVisible(measureFrame2, 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
	
	strayAdornee:Destroy()
	strayAdornee = nil
		
	clearTemporaryLines()
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


-----------------------------------
---------FUNCTION ACCESSORS--------
-----------------------------------

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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX37EE36A0E09B455A9461F14E76C51AE1">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Input</string>
				<string name="ScriptGuid"></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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX65E143CBEB744869B17274F381CA0B5B">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">Rubberband</string>
				<string name="ScriptGuid"></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(parent, startPos, endPos, partsList)
	local children = parent:GetChildren()
	local camera = workspace.CurrentCamera
	
	local partsList = partsList or {}
	
	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).p
			isLocked = false
		elseif v:IsA("Folder") then
			findPartsInSelection(v, startPos, endPos, partsList)
		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(partsList, v)
				end
			end
			
		end
		
	end
	return partsList
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(workspace, 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>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXEC7C6085619E450693F32FA161266FC2">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">RecyclingBin</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[-- @CloneTrooper1019 <3

local recyclingBin = {}
local bin = {}
local expirationTime = 5

local HttpService = game:GetService("HttpService")

function recyclingBin:Recycle(object)
	local class = object.ClassName
	local classBin = bin[class]
	if not classBin then
		classBin = {}
		bin[class] = classBin
	end
	local expirationGuid = HttpService:GenerateGUID(false)
	classBin[object] = expirationGuid;
	delay(expirationTime,function ()
		if classBin[object] == expirationGuid then
			classBin[object] = nil
			object:Destroy()
		end
	end)
	object.Parent = nil
end

function recyclingBin:RecycleObjects(objects)
	for _,v in pairs(objects) do
		self:Recycle(v)
	end	
end

function recyclingBin:Pull(objectType,parent)
	local object do
		local objects = bin[objectType]
		if objects then
			object = next(objects)
			if object then
				objects[object] = nil
			end
		end
	end
	if not object then
		object = Instance.new(objectType)
	end
	if parent then
		object.Parent = parent
	end
	return object
end

-- Allocate works similarly to Pull, except it tries to reuse objects that exist in the specified bin.
-- This removes the need to recycle every object during every frame.

function recyclingBin:Allocate(objectType,needed,allocationStorage)
	local allocation = {}
	local allocated = 0
	for _,v in pairs(allocationStorage:GetChildren()) do
		if v:IsA(objectType) then
			allocated = allocated + 1
			if allocated > needed then
				self:Recycle(v)
			else
				allocation[allocated] = v
			end
		end
	end
	while allocated < needed do
		allocated = allocated + 1
		allocation[allocated] = self:Pull(objectType,allocationStorage)
	end
	return allocation
end

return recyclingBin]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX981D853E5EE743829390EDD844D03ED2">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">FFlag</string>
				<string name="ScriptGuid">{0BB5FF8F-FDF0-4E96-936E-1100D5F32969}</string>
				<ProtectedString name="Source"><![CDATA[local flagMap = {}

-- void getFFlagValue(string flag)
local function getFFlagValue(flag)
    local flagExists, flagValue = pcall(function () return settings():GetFFlag(flag) end)
    flagMap[flag] = flagExists and flagValue
end


local FFlag = {}

-- bool FFlag:isEnabled(string flag)
function FFlag:isEnabled(flag)
    if (flagMap[flag] == nil) then
        getFFlagValue(flag)
    end
    
    return flagMap[flag]
end

return FFlag]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
	</Item>
</roblox>