Code/General/GameObjectExtensions.cs
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExtendedBox.General;

public static class GameObjectExtensions
{
    public static BBox GetBounds(this GameObject gameObject, Func<Component.IHasBounds, bool> predicate) =>
        gameObject.GetBounds<Component.IHasBounds>(predicate);
    public static BBox GetBounds<T>(this GameObject gameObject) where T : Component.IHasBounds =>
        gameObject.GetLocalBounds<T>().Transform(gameObject.WorldTransform);
    public static BBox GetBounds<T>(this GameObject gameObject, Func<T, bool> predicate) where T : Component.IHasBounds =>
        gameObject.GetLocalBounds(predicate).Transform(gameObject.WorldTransform);

    public static BBox GetLocalBounds(this GameObject gameObject, Func<Component.IHasBounds, bool> predicate) =>
        gameObject.GetLocalBounds<Component.IHasBounds>(predicate);
    public static BBox GetLocalBounds<T>(this GameObject gameObject) where T : Component.IHasBounds =>
        gameObject.GetLocalBounds<T>(x => true);
    public static BBox GetLocalBounds<T>(this GameObject gameObject, Func<T, bool> predicate) where T : Component.IHasBounds
    {
        IEnumerable<T> all = gameObject.Components.GetAll<T>().Where(x => predicate(x));
        if(!all.Any())
            return BBox.FromPositionAndSize(in Vector3.Zero, 1f);

        return BBox.FromBoxes(all.Select(x => ((Component.IHasBounds)x).LocalBounds));
    }

    public static string GetHierarchyAsString(this Component component, int depth = -1)
    {
        if(depth < 0)
            depth = int.MaxValue;

        if(component is null)
            return string.Empty;

        if(depth == 0)
            return component.GetType().Name;

        return new StringBuilder()
            .Append(GetHierarchyAsString(component.GameObject, depth - 1))
            .Append('.')
            .Append(component.GetType().Name)
            .ToString();
    }

    public static string GetHierarchyAsString(this GameObject gameObject, int depth = -1)
    {
        if(depth < 0)
            depth = int.MaxValue;

        StringBuilder stringBuilder = new();
        if(gameObject is not null)
        {
            stringBuilder.Insert(0, gameObject.Name);
            gameObject = gameObject.Parent;
        }

        while(depth > 0 && gameObject is not null)
        {
            stringBuilder.Insert(0, '.');
            stringBuilder.Insert(0, gameObject.Name);
            depth--;
            gameObject = gameObject.Parent;
        }
        return stringBuilder.ToString();
    }
}