Scaling Part from two points, mirrors weirdly when rotated

So I have a system where you Click one point, a preview block scales and anchors from one corner as the other follows your mouse until you click again, and places the block. The idea is you can create this part and scale it simply by clicking two points which will represent bottom left and top right corners(if looking from top down)

The problem is when the block is rotated, the preview gets all messed up and starts mirroring and acting weird. The final product is still as expected and works, but the live preview is what is messing up.

I will show a couple pictures to visualize and then share some code. In all pictures I will be moving the mouse four studs right and four studs up relative to the camera:

CORRECT:
image

The blue line is where my mouse currently is, the grey box is the first spot i clicked. I have this on a grid system. This is with no rotations.

INCORRECT:
Rotation of 90 degrees

Rotation of 180 Degrees

I will show one more picture here, at some angles(90 and 270 it seems) the scaling of the part doesn’t even anchor the main corner unless all sides are equal size.
image

This is not the case for the 0 rotation though it works perfect as intended. One corner stays on the light square where clicked and it scales as I move the mouse.

Here is the code for the preview block. This is not the first iteration, I have been fighting with this for hours.

startPosition and endPosition are vector3’s for the clicks of the mouse. End position represents where the mouse is currently.

local function updatePreviewBlock()
	if startPosition and endPosition then
		-- Calculate the delta vector in global space
		local deltaVector = Vector3.new(endPosition.X - startPosition.X, 0, endPosition.Z - startPosition.Z)

		-- Rotate the delta vector based on the current rotation
		local rotatedDeltaVector
		if currentRotation == 0 then
			rotatedDeltaVector = deltaVector
		elseif currentRotation == 90 then
			rotatedDeltaVector = Vector3.new(-deltaVector.Z, 0, deltaVector.X)
		elseif currentRotation == 180 then
			rotatedDeltaVector = Vector3.new(-deltaVector.X, 0, -deltaVector.Z)
		elseif currentRotation == 270 then
			rotatedDeltaVector = Vector3.new(deltaVector.Z, 0, -deltaVector.X)
		end

		-- Calculate the new size and position
		local sizeX = math.abs(rotatedDeltaVector.X)
		local sizeZ = math.abs(rotatedDeltaVector.Z)
		local sizeY = bottomHeight.Value

		-- The block's new position
		local blockPosition = startPosition + rotatedDeltaVector / 2

		-- Update the preview block's position and size
		bottomPreviewBlock.Size = Vector3.new(sizeX, sizeY, sizeZ)
		bottomPreviewBlock.Position = Vector3.new(blockPosition.X, initialY + sizeY / 2, blockPosition.Z)
		bottomPreviewBlock.Orientation = Vector3.new(0, currentRotation, 0)
	end
end

Any help would be really appreciated, bonus points if you can explain the solution as i struggle a bit with this kind of math stuff when programming and its more of a trial and error thing for me. Thank you :slight_smile:

Do a search of ‘gimbal lock’ on the forums. It’s an issue when CFraming where if you rotate only 1 axis of a 3 axis setup by a multiple of 90° the axes get locked together.
Some of those posts explain how to keep it from happening.

So it half worked thank you.

It now no longer mirrors and at 0 and 180 degrees it works as intended! But I think I still have my math wrong somewhere or i am not accounting for something because at 90 and 270 degrees it scales and moves unexpectedly. It’s not mirrored but it does not hold its position as it should and it scales in the wrong directions if the scale is not even on both sides.

I can send a clip that should be able to showcase it better than I can say it.

UNEXPECTED
RobloxStudioBeta_k67roiY3ji
For note,. this is rotated at 90 degrees.

Heres a quick show of how it should be:
EXPECTED
RobloxStudioBeta_B4npXZt6sC
This is not rotated.

EDIT:
Forgot the most important part, the updated code :grimacing:

local function QuaternionFromEulerYXZ(yaw, pitch, roll)
	local cy = math.cos(math.rad(yaw) * 0.5)
	local sy = math.sin(math.rad(yaw) * 0.5)
	local cp = math.cos(math.rad(pitch) * 0.5)
	local sp = math.sin(math.rad(pitch) * 0.5)
	local cr = math.cos(math.rad(roll) * 0.5)
	local sr = math.sin(math.rad(roll) * 0.5)

	local qx = sr * cp * cy - cr * sp * sy
	local qy = cr * sp * cy + sr * cp * sy
	local qz = cr * cp * sy - sr * sp * cy
	local qw = cr * cp * cy + sr * sp * sy

	return CFrame.new(0, 0, 0, qx, qy, qz, qw)
end

local function applyQuaternionRotation(part, rotationY)
	local quaternionRotation = QuaternionFromEulerYXZ(rotationY, 0, 0)
	part.CFrame = part.CFrame * quaternionRotation
end

local function updatePreviewBlock()
	if startPosition and endPosition then
		-- Calculate the size differences between start and end positions
		local sizeX = math.abs(endPosition.X - startPosition.X)
		local sizeZ = math.abs(endPosition.Z - startPosition.Z)
		local sizeY = bottomHeight.Value

		-- Midpoint position calculation
		local positionX = (startPosition.X + endPosition.X) / 2
		local positionZ = (startPosition.Z + endPosition.Z) / 2
		local positionY = initialY + sizeY / 2

		-- Update the preview block's position and size
		bottomPreviewBlock.Size = Vector3.new(sizeX, sizeY, sizeZ)
		bottomPreviewBlock.Position = Vector3.new(positionX, positionY, positionZ)

		-- Apply the quaternion rotation to the block
		applyQuaternionRotation(bottomPreviewBlock, currentRotation)

		if Settings.TopToggle.Value then
			local topSizeX = sizeX + (Settings.OverlapDistance.Value * 2)
			local topSizeZ = sizeZ + (Settings.OverlapDistance.Value * 2)
			local topSizeY = Settings.TopHeight.Value

			local topPosition = Vector3.new(positionX, initialY + sizeY + topSizeY / 2, positionZ)
			topPreviewBlock.Size = Vector3.new(topSizeX, topSizeY, topSizeZ)
			topPreviewBlock.Position = topPosition

			-- Apply the quaternion rotation to the top block
			applyQuaternionRotation(topPreviewBlock, currentRotation)
		end
	end
end