Is there a way to modify this part of the code, to be able to allow for vertical placement in the z axis too?
I have tried looping through all the parts in the canvas (the grid) and seeing which ones are in the same X and Y position as the model, but it does not work.
Any ideas?
Snippit of code calculating the cframe for the model render:
function Placement:CalcPlacementCFrame(model, position, rotation)
-- use other method to get info about the surface
local cf, size = self:CalcCanvas()
-- rotate the size so that we can properly constrain to the surface
local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.Size
modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))
-- get the position relative to the surface's CFrame
local lpos = cf:pointToObjectSpace(position);
-- the max bounds the model can be from the surface's center
local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
-- constrain the position using size2
local x = math.clamp(lpos.x, -size2.x, size2.x);
local y = math.clamp(lpos.y, -size2.y, size2.y);
local z = -modelSize.y/2
local g = self.GridUnit
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
local touching = model:GetTouchingParts()
-- create and return the CFrame
return cf * CFrame.new(x, y, z) * CFrame.Angles(-math.pi/2, rotation, 0)
end
Your CalcPlacementCFrame function is essentially constraining the model only along X and Y relative to the canvas surface and then placing it at a fixed z = -modelSize.y/2. Thats why it works fine for horizontal placement but dosent support stacking along Z
Treat the canvas as a 3D grid not just a 2D one
Track which “cells” along X, Y, Z are already occupied
Snap the model’s position along Z based on the highest occupied cell under the target X,Y
function Placement:CalcPlacementCFrame(model, position, rotation)
local cf, size = self:CalcCanvas()
-- rotate the size so that we can properly constrain to the surface
local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.Size
modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))
-- get the position relative to the surfaces CFrame thingi
local lpos = cf:pointToObjectSpace(position)
local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
-- constrain X and Y within the canvas
local x = math.clamp(lpos.x, -size2.x, size2.x)
local y = math.clamp(lpos.y, -size2.y, size2.y)
local g = self.GridUnit
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
-- find the highest Z at this X,Y to stack on top
local highestZ = 0
for _, part in ipairs(self.Canvas:GetChildren()) do
if part:IsA("BasePart") then
local partPos = cf:pointToObjectSpace(part.Position)
local dx = math.abs(partPos.x - x)
local dy = math.abs(partPos.y - y)
if dx < g/2 and dy < g/2 then
local topZ = part.Position.Z + part.Size.Z/2
if topZ > highestZ then
highestZ = topZ
end
end
end
end
-- place the model on top of the highest occupied cell
local z = highestZ + modelSize.y/2
-- create and return the CFrame thingi
return cf * CFrame.new(x, y, z) * CFrame.Angles(-math.pi/2, rotation, 0)
end
If im not stupid and this works this now supports vertical stacking on the Z axis while keeping X,Y snapping!!! Hope this helped you!!!