Stop Preview Part From Clipping With Placed Part

Hello Developers.

I’m currently experiencing an issue where my preview placement part keeps clipping with the placed part. I don’t want the preview part to be like this:

Right side


Left side

Front side

Back side

I tried to read this article but I’m clueless…

My current code:

local Players = game:GetService('Players')
local TweenService = game:GetService('TweenService')
local RunService = game:GetService('RunService')

local Packages = script.Parent.Parent.Packages

local Janitor = require(Packages.Janitor)
local Signal = require(Packages.DissatisfiedPSignal)
local Spring = require(Packages.Spring)
local EventsManager = require(script.Parent.Parent.Parent.Shared.Managers.EventsManager)

local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local HumanoidRootPart = Character:FindFirstChild('HumanoidRootPart') or Character.PrimaryPart or Character:WaitForChild('HumanoidRootPart')

local Mouse = Player:GetMouse()

local Camera = workspace.CurrentCamera

local CurrentActiveClass = nil

export type PlacementData = {
	Object: Instance,
	PrimaryPart: BasePart?,
	
	IsPlacing: boolean,
	SnapGridEnabled: boolean,
	
	ValidPlacementColor: Color3,
	InvalidPlacementColor: Color3,
	
	PlacementRange: number,
	Rotation: number,
	SnapGrid: number,
	ObjectLastHalfYSize: number,
	CurrentRotation: number,
	
	ObjectLastPosition: Vector3,
	ObjectLastSize: Vector3 | number,
	
	Cleaner: typeof(Janitor.new()),
	
	ObjectMoved: typeof(Signal.new()),
	ObjectPlaced: typeof(Signal.new()),
	PlacingStateChanged: typeof(Signal.new()),
	PlacementDestroying: typeof(Signal.new()),
	
	SetPlacingEnabled: (self: PlacementData, Value: boolean) -> (),
	SetSnapGridEnabled: (self: PlacementData, Value: boolean) -> (),
	ResetRotation: (self: PlacementData) -> (),
	Place: (self: PlacementData) -> (),
	Rotate: (self: PlacementData, CustomInfo: TweenInfo?) -> (),
	Destroy: (self: PlacementData) -> (),
}

local function CheckAndDeepFill<T>(GivenTbl: T, Source: T): T
	for Key, Value in Source do
		if typeof(Value) == 'table' then
			if typeof(GivenTbl[Key]) ~= 'table' then
				GivenTbl[Key] = {}
			end

			CheckAndDeepFill(GivenTbl[Key], Value)
		else
			if GivenTbl[Key] == nil or typeof(GivenTbl[Key]) ~= typeof(Value) then
				GivenTbl[Key] = Value
			end
		end
	end

	return GivenTbl
end

local function Snap(Value: number, Grid: number)
	return math.round(Value / Grid) * Grid
end

local MouseRaycastParams = RaycastParams.new()
MouseRaycastParams.FilterType = Enum.RaycastFilterType.Include
MouseRaycastParams.FilterDescendantsInstances = {workspace.PlacePlot, workspace.Placements}

local PlacementOverlapParams = OverlapParams.new()
PlacementOverlapParams.FilterType = Enum.RaycastFilterType.Include
PlacementOverlapParams.FilterDescendantsInstances = {workspace.Placements}

local Highlighter = Instance.new('Highlight')
Highlighter.Name = 'PlacementOutline'
Highlighter.OutlineTransparency = 0
Highlighter.FillTransparency = .75
Highlighter.FillColor = Color3.fromRGB(255, 0, 0)
Highlighter.OutlineColor = Color3.fromRGB(255, 0, 0)
Highlighter.DepthMode = Enum.HighlightDepthMode.Occluded

local Placement = {}
Placement.__index = Placement

if RunService:IsServer() then
	-- For auto-complete purpose.
	Placement = nil
	
	return Placement
end

-- Create new placement with given data(s).
function Placement.NewPlacement(Data: PlacementData) : PlacementData
	Data = Data or {}
	
	if typeof(Data.Object) ~= 'Instance' then
		warn(`Cannot found object or object is invalid in given placement data!`)
		return
	end
	
	local self = setmetatable(CheckAndDeepFill(Data, {
		IsPlacing = false,
		SnapGridEnabled = false,
		
		ValidPlacementColor = Color3.fromRGB(0, 255, 0),
		InvalidPlacementColor = Color3.fromRGB(255, 0, 0),
		
		PlacementRange = 100,
		Rotation = 15,
		SnapGrid = 1,
		CurrentRotation = 0,
	}), Placement)
	
	local Cleaner = Janitor.new()
	
	local function FastCreateSignal() return Cleaner:Add(Signal.new(), 'DisconnectAll') end
	
	self.Cleaner = Cleaner
	self.Object = Cleaner:Add(Data.Object:Clone())
	self.ObjectMoved = FastCreateSignal()
	self.ObjectPlaced = FastCreateSignal()
	self.PlacingStateChanged = FastCreateSignal()
	self.PlacementDestroying = FastCreateSignal()
	self.Object.Parent = nil
	
	if self.Object:IsA('Model') then
		self.ObjectLastHalfYSize = self.Object:GetExtentsSize().Y / (self.Object:GetExtentsSize().Y * 2)
		self.ObjectLastSize = self.Object:GetScale()
		self.ObjectLastPosition = self.Object:GetPivot().Position
		
		for _, Obj in self.Object:GetDescendants() do
			if self.Object.PrimaryPart and Obj:IsA('BasePart') then
				Obj.CanCollide = false
				Obj.Anchored = Obj == self.Object.PrimaryPart
			elseif not self.Object.PrimaryPart and Obj:IsA('BasePart') then
				Obj.CanCollide = false
				Obj.Anchored = true
			end
		end
		
		self.PrimaryPart = self.Object.PrimaryPart
	elseif self.Object:IsA('BasePart') then
		self.ObjectLastHalfYSize = self.Object.Size.Y / 2
		self.ObjectLastSize = self.Object.Size
		self.ObjectLastPosition = self.Object.Position
		self.Object.CanCollide = false
		self.Object.Anchored = true
	end
	
	return self
end

-- Toggle placing on & off.
function Placement.SetPlacingEnabled(self: PlacementData, Value: boolean)
	self.IsPlacing = Value
	
	local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
	local MouseCastResult = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * math.clamp(self.PlacementRange, 100, math.huge), MouseRaycastParams)
	
	if Value then
		if MouseCastResult then
			local X = MouseCastResult.Position.X
			local Y = MouseCastResult.Position.Y + self.ObjectLastHalfYSize
			local Z = MouseCastResult.Position.Z

			local CurrentRotation = self.CurrentRotation

			if self.Object:IsA('BasePart') then
				self.Object.CFrame = CFrame.new(Vector3.new(X, Y, Z)) * CFrame.Angles(0, math.rad(CurrentRotation), 0)
				self.Object.Size *= Vector3.new(.25, .25, .25)
				
				Spring.target(self.Object, .5, 6, {
					Size = self.ObjectLastSize,
				})
			elseif self.Object:IsA('Model') then
				self.Object:ScaleTo(self.Object:GetScale() * .25)
				
				local NumberValue = Instance.new('NumberValue')
				NumberValue.Value = self.Object:GetScale()
				
				NumberValue.Changed:Connect(function(NewValue: number)
					self.Object:ScaleTo(NewValue)
				end)
				
				Spring.target(NumberValue, .5, 6, {
					Value = self.ObjectLastSize,
				})
				
				Spring.completed(NumberValue, function()
					NumberValue:Destroy()
				end)
				
				self.Object:PivotTo(CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0))
			end
		end
		
		CurrentActiveClass = self
		Highlighter.Parent = self.Object
		self.Object.Parent = workspace
	else
		Spring.stop(self.Object)
		CurrentActiveClass = nil
		Highlighter.Parent = nil
		self.CurrentRotation = 0
		self.Object.Parent = nil
	end
end

-- Toggle snap grid on & off.
function Placement.SetSnapGridEnabled(self: PlacementData, Value: boolean)
	self.SnapGridEnabled = Value
end

-- Place the object down.
function Placement.Place(self: PlacementData)
	self.ObjectPlaced:Fire()
	
	-- Test
	if self.Object:IsA('Model') then
		EventsManager.PlaceObject:Fire('TestModel', {
			CFrame = self.Object:GetPivot(),
			Parent = workspace.Placements
		})
	elseif self.Object:IsA('BasePart') then
		EventsManager.PlaceObject:Fire('TestPart', {
			CFrame = self.Object.CFrame,
			Parent = workspace.Placements
		})
	end
end

function Placement.ResetRotation(self: PlacementData)
	if not self.IsPlacing then return end
	self.CurrentRotation = 0
end

-- Rotate the object.
function Placement.Rotate(self: PlacementData, CustomInfo: TweenInfo?)
	local Goal = {}
	
	self.CurrentRotation += self.Rotation
	
	if self.SnapGridEnabled then
		self.CurrentRotation = Snap(self.CurrentRotation, self.Rotation)
	end

	if self.CurrentRotation > 180 then
		self.CurrentRotation -= 360
	elseif self.CurrentRotation < -180 then
		self.CurrentRotation += 360
	end
	
	if self.Object:IsA('Model') then
		Goal.CFrame = self.Object:GetPivot() * CFrame.Angles(0, math.rad(self.Rotation), 0)
	elseif self.Object:IsA('BasePart') then
		Goal.CFrame = self.Object.CFrame * CFrame.Angles(0, math.rad(self.Rotation), 0)
	end
end

-- Destroy placement class and make it cannot be use anymore.
function Placement.Destroy(self: PlacementData)
	self.PlacementDestroying:Fire()
	if CurrentActiveClass == self then
		CurrentActiveClass = nil
	end
	Highlighter.Parent = nil
	self.Cleaner:Destroy()
end

RunService.PreSimulation:Connect(function()
	if CurrentActiveClass then
		local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
		local MouseCastResult = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 100000, MouseRaycastParams)

		if not MouseCastResult or not MouseCastResult.Instance or (HumanoidRootPart.Position - MouseCastResult.Position).Magnitude >= CurrentActiveClass.PlacementRange then return end
		
		local X = MouseCastResult.Position.X
		local Y = MouseCastResult.Position.Y + CurrentActiveClass.ObjectLastHalfYSize
		local Z = MouseCastResult.Position.Z
		
		if CurrentActiveClass.SnapGridEnabled then
			X = Snap(X, CurrentActiveClass.SnapGrid)
			Z = Snap(Z, CurrentActiveClass.SnapGrid)
		end	
		
		local CurrentRotation = CurrentActiveClass.CurrentRotation

		if CurrentActiveClass.Object:IsA('BasePart') then
			TweenService:Create(CurrentActiveClass.Object, TweenInfo.new(.06), {CFrame = CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0)}):Play()
		elseif CurrentActiveClass.Object:IsA('Model') then
			if CurrentActiveClass.PrimaryPart then
				TweenService:Create(CurrentActiveClass.PrimaryPart, TweenInfo.new(.06), {CFrame = CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0)}):Play()
			else
				CurrentActiveClass.Object:PivotTo(CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0))
			end
		end
	end
end)

return Placement
1 Like

Why don’t you just shapecast the object instead of using raycasting?

You could try scaling up the preview part ever so slightly so that each side keeps from Z-fighting by just a little bit. It wouldn’t look the best for complicated objects with a mixture of convex+concave shapes, but I hope this still helps.

Edit: Oohh, you meant to snap the object to the nearest outside face where it won’t intersect at all. Glad these other people came to the rescue!

Just to clarify, do you want the parts to avoid clipping (like in the post you linked), or do you want to avoid z-fighting?

I want the part to avoid clipping, not z-fighting. I think I found the solution for it. Sorry for the late reply. All I do is check if they overlap with each other by using spatial query.

local function IsOverlapping(Object: Instance)
	PlacementOverlapParams.FilterDescendantsInstances = {workspace.Placements}
	
	local ObjCFrame, ObjSize

	if Object:IsA('BasePart') then
		ObjCFrame = Object.CFrame
		ObjSize = Object.Size
	elseif Object:IsA('Model') then
		ObjCFrame, ObjSize = Object:GetBoundingBox()
	end
	
	--VisualBox(ObjCFrame, ObjSize - Vector3.one / 4)

	local Parts = workspace:GetPartBoundsInBox(ObjCFrame, ObjSize - Vector3.one / 2, PlacementOverlapParams)

	return #Parts > 0
end

RunService.PreSimulation:Connect(function()
	if CurrentActiveClass then
		local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
		local MouseCastResult = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 100000, MouseRaycastParams)

		if not MouseCastResult or not MouseCastResult.Instance or (HumanoidRootPart.Position - MouseCastResult.Position).Magnitude >= CurrentActiveClass.PlacementRange then return end
		
		local X = MouseCastResult.Position.X
		local Y = MouseCastResult.Position.Y + CurrentActiveClass.ObjectLastHalfYSize
		local Z = MouseCastResult.Position.Z
		
		if CurrentActiveClass.SnapGridEnabled then
			X = Snap(X, CurrentActiveClass.SnapGrid)
			Z = Snap(Z, CurrentActiveClass.SnapGrid)
			
			if MouseCastResult.Instance:IsDescendantOf(workspace.Placements) then
				Y = Snap(Y, CurrentActiveClass.SnapGrid)
			end
		end	
		
		local CurrentRotation = CurrentActiveClass.CurrentRotation
		
		local FinalCFrame = CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0)
		
		local Overlapping = IsOverlapping(CurrentActiveClass.Object)

		Highlighter.FillColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor
		Highlighter.OutlineColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor
		
		if CurrentActiveClass.Object:IsA('BasePart') then
			TweenService:Create(CurrentActiveClass.Object, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
		elseif CurrentActiveClass.Object:IsA('Model') then
			if CurrentActiveClass.PrimaryPart then
				TweenService:Create(CurrentActiveClass.PrimaryPart, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
			else
				CurrentActiveClass.Object:PivotTo(FinalCFrame)
			end
		end
	end
end)
1 Like

I’m glad you found the solution, but that will not avoid clipping, and it may be annoying to place the part in the desired position if it doesn’t automatically move to the nearest valid position.

That’s alright. I will try to read the post I linked above to find solution for it.

You don’t have to, though; I already suggested a solution for this. Simply shapecast the preview part to find a valid position.

I don’t understand why we use shapecast here to find a valid position. Can you give me an example?

Basically, shapecasting will raycast the entire volume of the instance, and it will return the first intersection, meaning that you will never receive a result with a position that clips with another object. Raycasting, on the other hand, is checking for intersections on a single ray, meaning that it may return a result with a position that causes the object to clip with another object, as seen in your original post. Try this implementation of shapecasting:

local function IsOverlapping(Object: Instance)
	PlacementOverlapParams.FilterDescendantsInstances = {workspace.Placements}

	local ObjCFrame, ObjSize

	if Object:IsA('BasePart') then
		ObjCFrame = Object.CFrame
		ObjSize = Object.Size
	elseif Object:IsA('Model') then
		ObjCFrame, ObjSize = Object:GetBoundingBox()
	end

	--VisualBox(ObjCFrame, ObjSize - Vector3.one / 4)

	local Parts = workspace:GetPartBoundsInBox(ObjCFrame, ObjSize - Vector3.one / 2, PlacementOverlapParams)

	return #Parts > 0
end

RunService.PreSimulation:Connect(function()
	if CurrentActiveClass then
		local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
		CurrentActiveClass.Object.Position = UnitRay.Origin
		local MouseCastResult = workspace:Shapecast(CurrentActiveClass.Object, UnitRay.Direction * 1000000, MouseRaycastParams)

		if not MouseCastResult or not MouseCastResult.Instance or (HumanoidRootPart.Position - MouseCastResult.Position).Magnitude >= CurrentActiveClass.PlacementRange then return end
		
		local EndPosition = UnitRay.Direction * MouseCastResult.Distance
		local X = EndPosition.X
		local Y = EndPosition.Y 
		local Z = EndPosition.Z

		if CurrentActiveClass.SnapGridEnabled then
			X = Snap(X, CurrentActiveClass.SnapGrid)
			Z = Snap(Z, CurrentActiveClass.SnapGrid)

			if MouseCastResult.Instance:IsDescendantOf(workspace.Placements) then
				Y = Snap(Y, CurrentActiveClass.SnapGrid)
			end
		end	

		local CurrentRotation = CurrentActiveClass.CurrentRotation

		local FinalCFrame = CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0)

		local Overlapping = IsOverlapping(CurrentActiveClass.Object)

		Highlighter.FillColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor
		Highlighter.OutlineColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor

		if CurrentActiveClass.Object:IsA('BasePart') then
			TweenService:Create(CurrentActiveClass.Object, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
		elseif CurrentActiveClass.Object:IsA('Model') then
			if CurrentActiveClass.PrimaryPart then
				TweenService:Create(CurrentActiveClass.PrimaryPart, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
			else
				CurrentActiveClass.Object:PivotTo(FinalCFrame)
			end
		end
	end
end)

I left the overlapping check in there just in case, but it should never return true (unless there is like a single pixel that is clipping).

Video example:
Raycasting Has Never Been Better! | Roblox Generalized Shapecasts

I think ShapeCast will not be a solution in this case. Because CurrentActiveClass.Object can be a model or a part. I’m using a model right now.

Shapecast also have some limitations:

image

Alright. Bounding box won’t capture any details, though.

You won’t really need anything larger or further than that. You’re making a placement system right? You probably can’t even see that far.

Yooooo @Tranquananh2811, I finally figured it out! I made this function, and it works perfectly as far as I can tell!

local function Shapecast(Object: BasePart | Model, Direction: Vector3, Origin: Vector3?)
	if Object:IsA("BasePart") then
		Origin = Origin or Object.Position
		Object.Position = Origin
		
		local Result = workspace:Shapecast(Object, Direction, rayParams)
		if Result then
			Object.Position = Origin + (Direction.Unit * Result.Distance)
		end
		
		return Result
	elseif Object:IsA("Model") then
		local boxCFrame, boxSize = Object:GetBoundingBox()
		local boxOffset = boxCFrame:PointToObjectSpace(Object:GetPivot().Position)

		local Result = workspace:Blockcast(CFrame.new(Origin) * boxCFrame.Rotation, boxSize, Direction, rayParams)
		if Result then
			Object:PivotTo(CFrame.new(Origin + (Direction.Unit * Result.Distance) + boxOffset) * boxCFrame.Rotation)
		end
		return Result
	end
end
Code Example
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local Mouse = game.Players.LocalPlayer:GetMouse()
local Camera = workspace.CurrentCamera
local HumanoidRootPart = game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart")

local object = workspace.Model--:WaitForChild("Part")

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {object}

local function Shapecast(Object: BasePart | Model, Direction: Vector3, Origin: Vector3?)
	if Object:IsA("BasePart") then
		Origin = Origin or Object.Position
		Object.Position = Origin
		
		local Result = workspace:Shapecast(Object, Direction, rayParams)
		if Result then
			Object.Position = Origin + (Direction.Unit * Result.Distance)
		end
		
		return Result
	elseif Object:IsA("Model") then
		local boxCFrame, boxSize = Object:GetBoundingBox()
		local boxOffset = boxCFrame:PointToObjectSpace(Object:GetPivot().Position)

		local Result = workspace:Blockcast(CFrame.new(Origin) * boxCFrame.Rotation, boxSize, Direction, rayParams)
		if Result then
			Object:PivotTo(CFrame.new(Origin + (Direction.Unit * Result.Distance) + boxOffset) * boxCFrame.Rotation)
		end
		return Result
	end
end

RunService.PreSimulation:Connect(function()
	local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
	local MouseCastResult = Shapecast(object, UnitRay.Direction * 1000, UnitRay.Origin)
end)

Let me know how it works!

Ahh. Thank you soo much for your help. I really appreciated it!

1 Like

Hello @ig1oo1 I got some new issue with this function now. The part/model height is very weird.

1 Like

In my old code I make the part/model to be at the mouse position and then do this using Raycast:

local X = MouseCastResult.Position.X
local Y = MouseCastResult.Position.Y + CurrentActiveClass.ObjectLastHalfYSize
local Z = MouseCastResult.Position.Z

Old code:

RunService.PreSimulation:Connect(function()
	if CurrentActiveClass then
		local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y)
		local MouseCastResult = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 100000, MouseRaycastParams)

		if not MouseCastResult or not MouseCastResult.Instance or (HumanoidRootPart.Position - MouseCastResult.Position).Magnitude >= CurrentActiveClass.PlacementRange then return end
		
		local X = MouseCastResult.Position.X
		local Y = MouseCastResult.Position.Y + CurrentActiveClass.ObjectLastHalfYSize
		local Z = MouseCastResult.Position.Z
		
		if CurrentActiveClass.SnapGridEnabled then
			X = Snap(X, CurrentActiveClass.SnapGrid)
			Z = Snap(Z, CurrentActiveClass.SnapGrid)
			
			if MouseCastResult.Instance:IsDescendantOf(workspace.Placements) then
				Y = Snap(Y, CurrentActiveClass.SnapGrid)
			end
		end	
		
		local CurrentRotation = CurrentActiveClass.CurrentRotation
		
		local FinalCFrame = CFrame.new(X, Y, Z) * CFrame.Angles(0, math.rad(CurrentRotation), 0)
		
		local Overlapping, TouchingParts = IsOverlapping(CurrentActiveClass.Object)

		Highlighter.FillColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor
		Highlighter.OutlineColor = Overlapping and CurrentActiveClass.InvalidPlacementColor or CurrentActiveClass.ValidPlacementColor
		
		if Overlapping then
			return
		end

		if CurrentActiveClass.Object:IsA('BasePart') then
			TweenService:Create(CurrentActiveClass.Object, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
		elseif CurrentActiveClass.Object:IsA('Model') then
			if CurrentActiveClass.PrimaryPart then
				TweenService:Create(CurrentActiveClass.PrimaryPart, TweenInfo.new(.06), {CFrame = FinalCFrame}):Play()
			else
				CurrentActiveClass.Object:PivotTo(FinalCFrame)
			end
		end
	end
end)

Hey there! Make sure you don’t add the object’s height/2—I already account for that in the function. Could you show me the code that you used in the video? Thanks.

I just lost my code because I forgot my code is in my place version’s place and not the main place, and closed the place version’s place window :skull:

Anyways. I will show it when I have time to rewrite the code. Thank you for your time.