YESS okay I found a good solution
I went and revisited this, and realised i can just use the other part as the ‘canvas’
Heres the completed code if anyone cares:
local Dragger = {ClassName = "Dragger"}
Dragger.__index = Dragger
function Dragger.new()
local self = {}
self.Target = nil
-- to be set manually
self.Increment = 0
self.RaycastRange = 100
setmetatable(self, Dragger)
return self
end
function Dragger:MouseDown(target: "Part/Model to move")
self.Target = target
end
function Dragger:CalcCanvas(canvas)
local canvasSize = canvas.Size
local up = Vector3.new(0, 1, 0)
local back = -self.SurfaceVector
local dot = back:Dot(Vector3.new(0, 1, 0))
local axis = (math.abs(dot) == 1) and Vector3.new(-dot, 0, 0) or up
local right = CFrame.fromAxisAngle(axis, math.pi/2) * back
local top = back:Cross(right).unit
local cf = canvas.CFrame * CFrame.fromMatrix(-back*canvasSize/2, right, top, back)
local size = Vector2.new((canvasSize * right).magnitude, (canvasSize * top).magnitude)
return cf, size
end
function Dragger:CalcPlacementCFrame(position, rotation, surfaceVector, canvas)
self.SurfaceVector = surfaceVector
local cf, size = self:CalcCanvas(canvas)
local tSize = self.Target.Size
if self.Target.ClassName == "Model" then tSize = self.Target.PrimaryPart.Size end
local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * tSize
modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))
local lpos = cf:pointToObjectSpace(position);
local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
if size2.x < 0 or size2.y < 0 then return end
local x = math.clamp(lpos.x, -size2.x, size2.x);
local y = math.clamp(lpos.y, -size2.y, size2.y);
local g = self.Increment
if (g > 0) then
x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
end
return cf * CFrame.new(x, y, -modelSize.y/2) * CFrame.Angles(-math.pi/2, rotation, 0)
end
function Dragger:MouseMove(unitRay: Ray, params: RaycastParams)
local rayStart = unitRay.Origin
local rayDirection = unitRay.Direction
if self.Target then
if not params then
params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.IgnoreWater = true
params.FilterDescendantsInstances = {self.Target}
end
local hit = game.Workspace:Raycast(rayStart, rayDirection * self.RaycastRange, params)
if hit and hit.Instance and hit.Instance ~= game.Workspace.Terrain then
local pos = hit.Position
local surface
local dotY = hit.Normal:Dot(Vector3.new(0,1,0))
local dotX = hit.Normal:Dot(Vector3.new(1,0,0))
local dotZ = hit.Normal:Dot(Vector3.new(0,0,1))
if (math.abs(dotY) >= 0.9) then
if (dotY < 0) then
surface = Enum.NormalId.Bottom
else
surface = Enum.NormalId.Top
end
elseif (math.abs(dotX) >= 0.9) then
if (dotX < 0) then
surface = Enum.NormalId.Left
else
surface = Enum.NormalId.Right
end
else
if (dotZ < 0) then
surface = Enum.NormalId.Front
else
surface = Enum.NormalId.Back
end
end
local c = self:CalcPlacementCFrame(pos, 0, Vector3.FromNormalId(surface), hit.Instance)
if not c then return end
if self.Target.ClassName == "Model" then
self.Target:PivotTo(c)
else
self.Target.CFrame = c
end
end
else
warn("No target was found.")
end
end
function Dragger:MouseUp()
self.Target = nil
end
return Dragger