// TODO: Figure out how best to collapse the InsertionGroup after the load task completes
        public InsertionGroupObject3D(IEnumerable <ILibraryItem> items,
                                      View3DWidget view3DWidget,
                                      InteractiveScene scene,
                                      Vector2 newItemOffset,
                                      Action <IObject3D, IEnumerable <IObject3D> > layoutParts,
                                      bool trackSourceFiles = false)
        {
            if (items == null)
            {
                return;
            }

            this.layoutParts = layoutParts;

            // Add a temporary placeholder to give us some bounds
            this.scene        = scene;
            this.view3DWidget = view3DWidget;

            this.LoadingItemsTask = Task.Run(async() =>
            {
                var offset = Matrix4X4.Identity;

                // Add the placeholder 'Loading...' object
                var placeholderItem = new Object3D()
                {
                    Mesh   = placeHolderMesh,
                    Matrix = Matrix4X4.Identity,
                    Parent = this
                };

                this.Children.Add(placeholderItem);

                var itemsToLoad = items.Where(item => item.IsContentFileType()).ToList();
                // Filter to content file types only
                foreach (var item in itemsToLoad)
                {
                    // Acquire
                    var progressControl = new DragDropLoadProgress(view3DWidget, null, ApplicationController.Instance.Theme);

                    // Position at accumulating offset
                    placeholderItem.Matrix        *= Matrix4X4.CreateTranslation(newItemOffset.X, (double)newItemOffset.Y, 0);
                    placeholderItem.Visible        = true;
                    progressControl.TrackingObject = placeholderItem;

                    var loadedItem = await item.CreateContent(progressControl.ProgressReporter);
                    if (loadedItem != null)
                    {
                        var aabb = loadedItem.GetAxisAlignedBoundingBox();

                        // lets move the cube to the center of the loaded thing
                        placeholderItem.Matrix *= Matrix4X4.CreateTranslation(-10 + aabb.XSize / 2, 0, 0);

                        placeholderItem.Visible = false;

                        // Copy scale/rotation/translation from the source and Center
                        loadedItem.Matrix = loadedItem.Matrix * Matrix4X4.CreateTranslation((double)-aabb.Center.X, (double)-aabb.Center.Y, (double)-aabb.MinXYZ.Z) * placeholderItem.Matrix;

                        // check if the item has 0 height (it is probably an image)
                        if (loadedItem.ZSize() == 0)
                        {
                            // raise it up a bit so it is not z fighting with the bed
                            loadedItem.Matrix *= Matrix4X4.CreateTranslation(0, 0, .1);
                        }

                        loadedItem.Color = loadedItem.Color;

                        // Set mesh path if tracking requested
                        if (trackSourceFiles &&
                            item is FileSystemFileItem fileItem &&
                            item.IsMeshFileType())
                        {
                            loadedItem.MeshPath = fileItem.Path;
                        }

                        this.Children.Add(loadedItem);

                        loadedItem.MakeNameNonColliding();

                        // Adjust next item position
                        newItemOffset.X = loadedItem.GetAxisAlignedBoundingBox().XSize / 2 + 10;
                    }

                    // the 1.3 is so the progress bar will collapse after going past 1
                    progressControl.ProgressReporter(1.3, "");
                }

                this.Children.Remove(placeholderItem);
                this.Collapse();

                this.Invalidate(InvalidateType.Children);
            });
        }
        // TODO: Figure out how best to collapse the InsertionGroup after the load task completes
        public InsertionGroupObject3D(IEnumerable <ILibraryItem> items, View3DWidget view3DWidget, InteractiveScene scene, Vector2 bedCenter, Func <bool> dragOperationActive, bool trackSourceFiles = false)
        {
            if (items == null)
            {
                return;
            }

            // Add a temporary placeholder to give us some bounds
            this.scene        = scene;
            this.view3DWidget = view3DWidget;

            this.LoadingItemsTask = Task.Run((Func <Task>)(async() =>
            {
                var newItemOffset = Vector2.Zero;
                if (dragOperationActive != null &&
                    !dragOperationActive())
                {
                    newItemOffset = bedCenter;
                }

                var offset = Matrix4X4.Identity;

                // Add the placeholder 'Loading...' object
                var placeholderItem = new Object3D()
                {
                    Mesh   = placeHolderMesh,
                    Matrix = Matrix4X4.Identity,
                    Parent = this
                };

                this.Children.Add(placeholderItem);

                // Filter to content file types only
                foreach (var item in items.Where(item => item.IsContentFileType()).ToList())
                {
                    // Acquire
                    var progressControl = new DragDropLoadProgress(view3DWidget, null);

                    // Position at accumulating offset
                    placeholderItem.Matrix        *= Matrix4X4.CreateTranslation(newItemOffset.X, (double)newItemOffset.Y, 0);
                    placeholderItem.Visible        = true;
                    progressControl.TrackingObject = placeholderItem;

                    var loadedItem = await item.CreateContent(progressControl.ProgressReporter);
                    if (loadedItem != null)
                    {
                        var aabb = loadedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity);

                        // lets move the cube to the center of the loaded thing
                        placeholderItem.Matrix *= Matrix4X4.CreateTranslation(-10 + aabb.XSize / 2, 0, 0);

                        placeholderItem.Visible = false;

                        // Copy scale/rotation/translation from the source and Center
                        loadedItem.Matrix = loadedItem.Matrix * Matrix4X4.CreateTranslation((double)-aabb.Center.X, (double)-aabb.Center.Y, (double)-aabb.minXYZ.Z) * placeholderItem.Matrix;

                        // check if the item has 0 height (it is probably an image)
                        if (loadedItem.ZSize() == 0)
                        {
                            // raise it up a bit so it is not z fighting with the bed
                            loadedItem.Matrix *= Matrix4X4.CreateTranslation(0, 0, .1);
                        }

                        loadedItem.Color = loadedItem.Color;

                        // Set mesh path if tracking requested
                        if (trackSourceFiles &&
                            item is FileSystemFileItem fileItem &&
                            item.IsMeshFileType())
                        {
                            loadedItem.MeshPath = fileItem.Path;
                        }

                        // Notification should force invalidate and redraw
                        //progressReporter?.Invoke(1, "");

                        this.Children.Add(loadedItem);

                        loadedItem.MakeNameNonColliding();

                        // Wait for content to load

                        // Adjust next item position
                        // TODO: do something more interesting than increment in x
                        newItemOffset.X = loadedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity).XSize / 2 + 10;
                    }

                    progressControl.ProgressReporter(1.3, "");
                }

                this.Children.Remove(placeholderItem);

                this.ContentAcquired = true;

                ContentLoaded?.Invoke(this, null);

                if (dragOperationActive != null &&
                    !dragOperationActive())
                {
                    this.Collapse();
                }

                this.Invalidate(new InvalidateArgs(this, InvalidateType.Content));
            }));
        }