AEyeXActorBase* AEyeXPlayerController::FindByBoxedLineTrace(FHitResult& OutHit, const FSceneView* const View, const FVector2D& GazePoint,
	const FCollisionObjectQueryParams& ObjectParams, const FCollisionQueryParams& TraceParams)
{
	UWorld* World = GetWorld();
	if (!World) return nullptr;

	// Set up a box around the gaze point
	FVector2D Corners[4];
	GetBoxCorners(GazePoint, BoxSize * GetApproximatePixelsPerMillimeter(), Corners);

	// First check center point
	AEyeXActorBase* EyeXActor = nullptr;
	FVector Start, End;
	FEyeXUtils::GetStartAndEndOfLineTrace(View, MaxDistance, GazePoint, /*out*/ Start, /*out*/ End);
	if (World->LineTraceSingleByObjectType(OutHit, Start, End, ObjectParams, TraceParams))
	{
		EyeXActor = Cast<AEyeXActorBase>(OutHit.GetActor());
	}
	else
	{
		// If center point missed we perform traces in a box around the gaze point
		// and choose the closest EyeXActor hit by the traces
		TArray<AEyeXActorBase*> EyeXActors;
		for (int i = 0; i < 4; ++i)
		{
			FVector BoxStart, BoxEnd;
			FEyeXUtils::GetStartAndEndOfLineTrace(View, MaxDistance, Corners[i], /*out*/ BoxStart, /*out*/ BoxEnd);
			if (World->LineTraceSingleByObjectType(OutHit, BoxStart, BoxEnd, ObjectParams, TraceParams))
			{
				AEyeXActorBase* Actor = Cast<AEyeXActorBase>(OutHit.GetActor());
				if (!Actor) continue;
				EyeXActors.Add(Actor);
			}

			VisualizeHitTestPoint(bVisualizeDetection, World, BoxStart);
		}

		if (EyeXActors.Num() > 0)
		{
			FEyeXUtils::ActorDistanceComparer Comparer(PlayerCameraManager);
			EyeXActors.Sort(Comparer);
			EyeXActor = EyeXActors[0];
		}
	}

	VisualizeHit(bVisualizeDetection, World, OutHit);
	VisualizeGazePoint(bVisualizeDetection, World, Start);

	return EyeXActor;
}
AEyeXActorBase* AEyeXPlayerController::FindByFrustumIntersection(FHitResult& OutHit, const FSceneView* const View, const FVector2D& GazePoint,
	const FCollisionObjectQueryParams& ObjectParams, const FCollisionQueryParams& TraceParams)
{
	UWorld* World = GetWorld();
	if (!World) return nullptr;

	FVector2D Corners[4];
	GetBoxCorners(GazePoint, BoxSize * GetApproximatePixelsPerMillimeter(), Corners);

	// First do a ray cast from the gaze point to determine if something is blocking.
	// If we happen to find an AEyeXActorBase, we're done
	FVector Start, End;
	FEyeXUtils::GetStartAndEndOfLineTrace(View, MaxDistance, GazePoint, /*out*/ Start, /*out*/ End);

	AEyeXActorBase* EyeXActor = nullptr;
	if (World->LineTraceSingleByObjectType(OutHit, Start, End, ObjectParams, TraceParams))
	{
		EyeXActor = Cast<AEyeXActorBase>(OutHit.GetActor());
	}
	else
	{
		// Calculate frustum using the SceneView
		FConvexVolume Frustum;
		FEyeXMathHelpers::CalculateFrustum(Corners, View, Frustum);

		// Check frustum intersection with EyeX Actors
		TArray<AEyeXActorBase*> EyeXActors;
		GetEyeXActorsSortedByDistance(EyeXActors);
		for (AEyeXActorBase* Actor : EyeXActors)
		{
			if (FEyeXMathHelpers::IntersectsFrustum(OutHit, Actor, Frustum))
			{
				EyeXActor = Actor;
				break;
			}
		}
	}

	VisualizeHit(bVisualizeDetection, World, OutHit);
	VisualizeGazePoint(bVisualizeDetection, World, Start);

	return EyeXActor;
}
AEyeXActorBase* AEyeXPlayerController::FindByLineTrace(FHitResult& OutHit, const FSceneView* const View, const FVector2D& GazePoint, 
	const FCollisionObjectQueryParams& ObjectParams, const FCollisionQueryParams& TraceParams)
{
	UWorld* World = GetWorld();
	if (!World) return nullptr;

	AEyeXActorBase* EyeXActor = nullptr;
	FVector Start, End;
	FEyeXUtils::GetStartAndEndOfLineTrace(View, MaxDistance, GazePoint, /*out*/ Start, /*out*/ End);
	if (World->LineTraceSingleByObjectType(OutHit, Start, End, ObjectParams, TraceParams))
	{
		EyeXActor = Cast<AEyeXActorBase>(OutHit.GetActor());
	}

	VisualizeHit(bVisualizeDetection, World, OutHit);
	VisualizeGazePoint(bVisualizeDetection, World, Start);

	return EyeXActor;
}