public override void OnMouseMove(MouseEvent3DArgs mouseEvent3D)
        {
            var selectedItem = RootSelection;

            ActiveSelectedItem = selectedItem;
            if (MouseOver)
            {
                zValueDisplayInfo.Visible = true;
            }
            else if (!HadClickOnControl)
            {
                zValueDisplayInfo.Visible = false;
            }

            if (MouseDownOnControl)
            {
                IntersectInfo info = hitPlane.GetClosestIntersection(mouseEvent3D.MouseRay);

                if (info != null &&
                    selectedItem != null)
                {
                    AxisAlignedBoundingBox originalSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity);

                    Vector3 delta = info.HitPosition - initialHitPosition;

                    Vector3 newPosition = originalPointToMove + delta;

                    if (InteractionContext.SnapGridDistance > 0)
                    {
                        // snap this position to the grid
                        double snapGridDistance = InteractionContext.SnapGridDistance;

                        // snap this position to the grid
                        newPosition.Z = ((int)((newPosition.Z / snapGridDistance) + .5)) * snapGridDistance;
                    }

                    Vector3 topPosition  = GetTopPosition(selectedItem);
                    Vector3 lockedBottom = new Vector3(topPosition.X, topPosition.Y, originalSelectedBounds.MinXYZ.Z);

                    Vector3 newSize = Vector3.Zero;
                    newSize.Z = newPosition.Z - lockedBottom.Z;

                    // scale it
                    Vector3 scaleAmount = ScaleCornerControl.GetScalingConsideringShiftKey(originalSelectedBounds, mouseDownSelectedBounds, newSize, InteractionContext.GuiSurface.ModifierKeys);

                    Matrix4X4 scale = Matrix4X4.CreateScale(scaleAmount);

                    selectedItem.Matrix = selectedItem.ApplyAtBoundsCenter(scale);

                    // and keep the locked edge in place
                    AxisAlignedBoundingBox scaledSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity);
                    Vector3 newLockedBottom = new Vector3(topPosition.X, topPosition.Y, scaledSelectedBounds.MinXYZ.Z);

                    selectedItem.Matrix *= Matrix4X4.CreateTranslation(lockedBottom - newLockedBottom);

                    Invalidate();
                }
            }

            base.OnMouseMove(mouseEvent3D);
        }
        public ScaleTopControl(IInteractionVolumeContext context)
            : base(context)
        {
            zValueDisplayInfo = new InlineEditControl()
            {
                ForceHide = () =>
                {
                    // if the selection changes
                    if (RootSelection != ActiveSelectedItem)
                    {
                        return(true);
                    }

                    // if another control gets a hover
                    if (InteractionContext.HoveredInteractionVolume != this &&
                        InteractionContext.HoveredInteractionVolume != null)
                    {
                        return(true);
                    }


                    // if we clicked on the control
                    if (HadClickOnControl)
                    {
                        return(false);
                    }

                    return(false);
                }
                ,
                GetDisplayString = (value) => "{0:0.0}mm".FormatWith(value)
            };

            zValueDisplayInfo.VisibleChanged += (s, e) =>
            {
                if (!zValueDisplayInfo.Visible)
                {
                    HadClickOnControl = false;
                }
            };

            zValueDisplayInfo.EditComplete += (s, e) =>
            {
                var selectedItem = ActiveSelectedItem;

                Matrix4X4 startingTransform      = selectedItem.Matrix;
                var       originalSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity);
                Vector3   topPosition            = GetTopPosition(selectedItem);
                Vector3   lockedBottom           = new Vector3(topPosition.X, topPosition.Y, originalSelectedBounds.MinXYZ.Z);

                Vector3 newSize = Vector3.Zero;
                newSize.Z = zValueDisplayInfo.Value;
                Vector3 scaleAmount = ScaleCornerControl.GetScalingConsideringShiftKey(originalSelectedBounds, mouseDownSelectedBounds, newSize, InteractionContext.GuiSurface.ModifierKeys);

                Matrix4X4 scale = Matrix4X4.CreateScale(scaleAmount);

                selectedItem.Matrix = selectedItem.ApplyAtBoundsCenter(scale);

                // and keep the locked edge in place
                AxisAlignedBoundingBox scaledSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(Matrix4X4.Identity);
                Vector3 newLockedBottom = new Vector3(topPosition.X, topPosition.Y, scaledSelectedBounds.MinXYZ.Z);

                selectedItem.Matrix *= Matrix4X4.CreateTranslation(lockedBottom - newLockedBottom);

                Invalidate();

                InteractionContext.Scene.AddTransformSnapshot(startingTransform);
            };

            InteractionContext.GuiSurface.AddChild(zValueDisplayInfo);

            DrawOnTop = true;

            topScaleMesh = PlatonicSolids.CreateCube(selectCubeSize, selectCubeSize, selectCubeSize);

            CollisionVolume = topScaleMesh.CreateTraceData();

            InteractionContext.GuiSurface.AfterDraw += InteractionLayer_AfterDraw;
        }
        public ScaleHeightControl(IObject3DControlContext context)
            : base(context)
        {
            theme = AppContext.Theme;

            zValueDisplayInfo = new InlineEditControl()
            {
                ForceHide = () =>
                {
                    // if the selection changes
                    if (RootSelection != activeSelectedItem)
                    {
                        return(true);
                    }

                    // if another control gets a hover
                    if (Object3DControlContext.HoveredObject3DControl != this &&
                        Object3DControlContext.HoveredObject3DControl != null)
                    {
                        return(true);
                    }

                    // if we clicked on the control
                    if (hadClickOnControl)
                    {
                        return(false);
                    }

                    return(false);
                },
                GetDisplayString = (value) => "{0:0.0}".FormatWith(value)
            };

            zValueDisplayInfo.VisibleChanged += (s, e) =>
            {
                if (!zValueDisplayInfo.Visible)
                {
                    hadClickOnControl = false;
                }
            };

            zValueDisplayInfo.EditComplete += (s, e) =>
            {
                var selectedItem = activeSelectedItem;

                if (selectedItem is IObjectWithHeight heightObject)
                {
                    var     startingHeight         = heightObject.Height;
                    var     originalSelectedBounds = selectedItem.GetAxisAlignedBoundingBox();
                    Vector3 topPosition            = GetTopPosition(selectedItem);
                    var     lockedBottom           = GetBottomPosition(selectedItem);

                    Vector3 newSize = Vector3.Zero;
                    newSize.Z = zValueDisplayInfo.Value;
                    Vector3 scaleAmount = ScaleCornerControl.GetScalingConsideringShiftKey(originalSelectedBounds, mouseDownSelectedBounds, newSize, Object3DControlContext.GuiSurface.ModifierKeys);

                    heightObject.Height *= scaleAmount.Z;

                    var scale = Matrix4X4.CreateScale(scaleAmount);

                    Invalidate();

                    SetHeightUndo(startingHeight);
                }
            };

            Object3DControlContext.GuiSurface.AddChild(zValueDisplayInfo);

            DrawOnTop = true;

            topScaleMesh = PlatonicSolids.CreateCube(arrowSize, arrowSize, arrowSize);

            CollisionVolume = topScaleMesh.CreateBVHData();

            Object3DControlContext.GuiSurface.BeforeDraw += Object3DControl_BeforeDraw;
        }