private (Vector3 start0, Vector3 end0, Vector3 start1, Vector3 end1) GetMeasureLine(int quadrant)
        {
            var selectedItem = RootSelection;
            var corner       = new Vector3[4];
            var screen       = new Vector3[4];

            for (int i = 0; i < 4; i++)
            {
                corner[i] = ObjectSpace.GetCornerPosition(selectedItem, quadrant + i);
                screen[i] = Object3DControlContext.World.WorldToScreenSpace(corner[i]);
            }

            var start     = corner[0];
            var direction = (start - corner[1]).GetNormal();
            var end       = corner[3];

            // find out which side we should render on (the one closer to the screen)
            if (screen[0].Z > screen[1].Z)
            {
                start     = corner[1];
                end       = corner[2];
                direction = (start - corner[0]).GetNormal();
            }

            var startScale = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(start);
            var endScale   = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(end);
            var offset     = .3;
            var start0     = start + direction * LineLength * offset * startScale;
            var end0       = start + direction * LineLength * (1 + offset) * endScale;
            var start1     = end + direction * LineLength * offset * endScale;
            var end1       = end + direction * LineLength * (1 + offset) * endScale;

            return(start0, end0, start1, end1);
        }
        private async void EditComplete(object s, EventArgs e)
        {
            var newWidth = xValueDisplayInfo.Value != 0 ? xValueDisplayInfo.Value : scaleController.FinalState.Width;
            var newDepth = yValueDisplayInfo.Value != 0 ? yValueDisplayInfo.Value : scaleController.FinalState.Depth;

            if (newWidth == scaleController.FinalState.Width &&
                newDepth == scaleController.FinalState.Depth)
            {
                return;
            }

            var lockedEdge = ObjectSpace.GetCornerPosition(ActiveSelectedItem, quadrantIndex + 2);

            scaleController.ScaleWidthDepth(newWidth, newDepth);
            await ActiveSelectedItem.Rebuild();

            // and keep the locked edge in place
            var newLockedEdge = ObjectSpace.GetCornerPosition(ActiveSelectedItem, quadrantIndex + 2);

            ActiveSelectedItem.Translate(lockedEdge - newLockedEdge);

            scaleController.EditComplete();
            // make a new controller so we will have new undo data
            scaleController = new ScaleController(Object3DControlContext, getWidth, setWidth, getDepth, setDepth, getHeight, setHeight);
        }
        public override void Draw(DrawGlContentEventArgs e)
        {
            bool shouldDrawScaleControls = true;

            if (Object3DControlContext.SelectedObject3DControl != null &&
                Object3DControlContext.SelectedObject3DControl as ScaleWidthDepthCornerControl == null)
            {
                shouldDrawScaleControls = false;
            }

            var selectedItem = RootSelection;

            if (selectedItem != null)
            {
                // Ensures that functions in this scope run against the original instance reference rather than the
                // current value, thus avoiding null reference errors that would occur otherwise

                if (shouldDrawScaleControls)
                {
                    // don't draw if any other control is dragging
                    if (MouseIsOver || MouseDownOnControl)
                    {
                        GLHelper.Render(minXminYMesh, theme.PrimaryAccentColor.WithAlpha(e.Alpha0to255), TotalTransform, RenderTypes.Shaded);
                    }
                    else
                    {
                        GLHelper.Render(minXminYMesh, theme.TextColor.WithAlpha(e.Alpha0to255), TotalTransform, RenderTypes.Shaded);
                    }
                }

                if (hitPlane != null)
                {
                    //Object3DControlContext.World.RenderPlane(hitPlane.Plane, Color.Red, true, 30, 3);
                    //Object3DControlContext.World.RenderPlane(initialHitPosition, hitPlane.Plane.Normal, Color.Red, true, 30, 3);
                }

                if (e != null)
                {
                    Vector3 startPosition = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex);
                    Vector3 endPosition   = ObjectSpace.GetCornerPosition(selectedItem, (quadrantIndex + 1) % 4);
                    Object3DControlContext.World.Render3DLine(startPosition, endPosition, theme.TextColor.WithAlpha(e.Alpha0to255), e.ZBuffered, GuiWidget.DeviceScale);
                }

                if (MouseIsOver || MouseDownOnControl)
                {
                    DrawMeasureLines(e, quadrantIndex);
                    DrawMeasureLines(e, quadrantIndex + 1);
                }
            }

            base.Draw(e);
        }
        public override void SetPosition(IObject3D selectedItem, MeshSelectInfo selectInfo)
        {
            // create the transform for the box
            Vector3 cornerPosition = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex);

            Vector3 boxCenter = cornerPosition;

            double distBetweenPixelsWorldSpace = Object3DControlContext.World.GetWorldUnitsPerScreenPixelAtPosition(cornerPosition);

            switch (quadrantIndex)
            {
            case 0:
                boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 1:
                boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 2:
                boxCenter.X -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;

            case 3:
                boxCenter.X += selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                boxCenter.Y -= selectCubeSize / 2 * distBetweenPixelsWorldSpace;
                break;
            }

            boxCenter.Z += selectCubeSize / 2 * distBetweenPixelsWorldSpace;

            var rotation = Matrix4X4.CreateRotation(new Quaternion(selectedItem.Matrix));

            var centerMatrix = Matrix4X4.CreateTranslation(boxCenter);

            centerMatrix   = rotation * Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * centerMatrix;
            TotalTransform = centerMatrix;
        }
        private Vector3 GetDeltaToOtherSideXy(IObject3D selectedItem, int quadrantIndex)
        {
            Vector3 cornerPosition    = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex);
            Vector3 cornerPositionCcw = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 1);
            Vector3 cornerPositionCw  = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 3);

            double xDirection = cornerPositionCcw.X - cornerPosition.X;

            if (xDirection == 0)
            {
                xDirection = cornerPositionCw.X - cornerPosition.X;
            }

            double yDirection = cornerPositionCcw.Y - cornerPosition.Y;

            if (yDirection == 0)
            {
                yDirection = cornerPositionCw.Y - cornerPosition.Y;
            }

            return(new Vector3(xDirection, yDirection, cornerPosition.Z));
        }
        public override async void OnMouseMove(Mouse3DEventArgs mouseEvent3D, bool mouseIsOver)
        {
            var selectedItem = RootSelection;

            ActiveSelectedItem = selectedItem;

            if (MouseIsOver || MouseDownOnControl)
            {
                xValueDisplayInfo.Visible = true;
                yValueDisplayInfo.Visible = true;
            }
            else if (!hadClickOnControl || scaleController.HasChange)
            {
                xValueDisplayInfo.Visible = false;
                yValueDisplayInfo.Visible = false;
            }

            if (MouseDownOnControl && hitPlane != null)
            {
                var info = hitPlane.GetClosestIntersection(mouseEvent3D.MouseRay);

                if (info != null &&
                    selectedItem != null)
                {
                    var lockedEdge = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 2);

                    var delta = info.HitPosition - initialHitPosition;

                    var corner0     = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex);
                    var corner1     = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 1);
                    var corner3     = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 3);
                    var direction01 = (corner0 - corner1).GetNormal();
                    var direction03 = (corner0 - corner3).GetNormal();

                    var deltaAlong01 = direction01.Dot(delta);
                    var deltaAlong03 = direction03.Dot(delta);

                    // scale it
                    var newSize = new Vector2(scaleController.InitialState.Width, scaleController.InitialState.Depth);
                    if (quadrantIndex % 2 == 0)
                    {
                        newSize.X += deltaAlong01;
                        newSize.X  = Math.Max(Math.Max(newSize.X, .001), Object3DControlContext.SnapGridDistance);

                        newSize.Y += deltaAlong03;
                        newSize.Y  = Math.Max(Math.Max(newSize.Y, .001), Object3DControlContext.SnapGridDistance);
                    }
                    else
                    {
                        newSize.X += deltaAlong03;
                        newSize.X  = Math.Max(Math.Max(newSize.X, .001), Object3DControlContext.SnapGridDistance);

                        newSize.Y += deltaAlong01;
                        newSize.Y  = Math.Max(Math.Max(newSize.Y, .001), Object3DControlContext.SnapGridDistance);
                    }

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

                        // snap this position to the grid
                        newSize.X = ((int)((newSize.X / snapGridDistance) + .5)) * snapGridDistance;
                        newSize.Y = ((int)((newSize.Y / snapGridDistance) + .5)) * snapGridDistance;
                    }

                    scaleController.ScaleWidthDepth(newSize.X, newSize.Y);

                    await selectedItem.Rebuild();

                    // and keep the locked edge in place
                    var newLockedEdge = ObjectSpace.GetCornerPosition(selectedItem, quadrantIndex + 2);

                    selectedItem.Matrix *= Matrix4X4.CreateTranslation(lockedEdge - newLockedEdge);

                    Invalidate();
                }
            }

            base.OnMouseMove(mouseEvent3D, mouseIsOver);
        }