Beispiel #1
0
        protected override void OnMouseMove( MouseEventArgs e )
        {
            base.OnMouseMove( e );

            m_MousePositionCurrent = e.Location;
            if ( m_MouseButtonsDown == MouseButtons.None )
            {
                m_ButtonDownMousePosition = e.Location;
                m_ButtonDownCropRectangleCenter = m_CropRectangleCenter;
                m_ButtonDownCropRectangleHalfSize = m_CropRectangleHalfSize;
                m_ButtonDownCropRectangleRotation = m_CropRectangleRotation;
                m_ButtonDownCropRectangleAxisX = m_CropRectangleAxisX;
                m_ButtonDownCropRectangleAxisY = m_CropRectangleAxisY;

                m_ButtonDownClientCropRectangleCenter = m_ClientCropRectangleCenter;
                m_ButtonDownClientCropRectangleVertices = new PointF[4] {
                    m_ClientCropRectangleVertices[0],
                    m_ClientCropRectangleVertices[1],
                    m_ClientCropRectangleVertices[2],
                    m_ClientCropRectangleVertices[3],
                };
            }

            switch ( m_ManipulationState )
            {
                case MANIPULATION_STATE.CROP_RECTANGLE:
                {	// Take care of crop rectangle manipulation
                    if ( !m_CropRectangleManipulationStarted )
                    {	// Update the cursor based on the hovered manipulation spot
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.NONE;

                        const float	TOLERANCE = 8.0f;

                        PointF	P = e.Location;

                        PointF	TopLeftCorner2UV = new PointF( P.X - m_ClientCropRectangleVertices[0].X, P.Y - m_ClientCropRectangleVertices[0].Y );
                        PointF	BottomRightCorner2UV = new PointF( P.X - m_ClientCropRectangleVertices[3].X, P.Y - m_ClientCropRectangleVertices[3].Y );

                        float	Distance2Left = -TopLeftCorner2UV.X * m_CropRectangleAxisX.x - TopLeftCorner2UV.Y * m_CropRectangleAxisX.y;
                        float	Distance2Top = TopLeftCorner2UV.X * m_CropRectangleAxisY.x + TopLeftCorner2UV.Y * m_CropRectangleAxisY.y;
                        float	Distance2Right = BottomRightCorner2UV.X * m_CropRectangleAxisX.x + BottomRightCorner2UV.Y * m_CropRectangleAxisX.y;
                        float	Distance2Bottom = -BottomRightCorner2UV.X * m_CropRectangleAxisY.x - BottomRightCorner2UV.Y * m_CropRectangleAxisY.y;

                        // Stretch
                        if ( Math.Abs( Distance2Left ) < TOLERANCE )
                        {
                            if ( Math.Abs( Distance2Top ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT;
                            else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT;
                            else
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.LEFT;
                        }
                        else if ( Math.Abs( Distance2Right ) < TOLERANCE )
                        {
                            if ( Math.Abs( Distance2Top ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT;
                            else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT;
                            else
                                m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.RIGHT;
                        }
                        else if ( Math.Abs( Distance2Top ) < TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.TOP;
                        else if ( Math.Abs( Distance2Bottom ) < TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.BOTTOM;
                        // Rotate
                        else if ( Distance2Top > TOLERANCE && Distance2Right > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT;
                        else if ( Distance2Top > TOLERANCE && Distance2Left > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT;
                        else if ( Distance2Bottom > TOLERANCE && Distance2Right > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT;
                        else if ( Distance2Bottom > TOLERANCE && Distance2Left > TOLERANCE )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT;
                        else if ( Distance2Left < 0.0f && Distance2Right < 0.0f && Distance2Bottom < 0.0f && Distance2Top < 0.0f )
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CENTER;

                        // Update cursor accordingly
                        switch ( m_CropRectangleManipulatedSpot  )
                        {
                            case CROP_RECTANGLE_SPOT.NONE: Cursor = Cursors.Default; break;
                            case CROP_RECTANGLE_SPOT.LEFT: Cursor = Cursors.SizeWE; break;
                            case CROP_RECTANGLE_SPOT.RIGHT: Cursor = Cursors.SizeWE; break;
                            case CROP_RECTANGLE_SPOT.TOP: Cursor = Cursors.SizeNS; break;
                            case CROP_RECTANGLE_SPOT.BOTTOM: Cursor = Cursors.SizeNS; break;
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Cursor = Cursors.SizeNWSE; break;
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Cursor = Cursors.SizeNWSE; break;
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Cursor = Cursors.SizeNESW; break;
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Cursor = Cursors.SizeNESW; break;
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                                Cursor = Cursors.Hand;
                                break;
                            case CROP_RECTANGLE_SPOT.CENTER:
                                Cursor = Cursors.Hand;
                                break;
                        }
                    }
                    else
                    {	// Handle actual manipulation
                        ImageUtility.float2		CurClientPos = new ImageUtility.float2( e.X, e.Y );
                        ImageUtility.float2		OldClientPos = new ImageUtility.float2( m_ButtonDownMousePosition.X, m_ButtonDownMousePosition.Y );
                        ImageUtility.float2		OldClientCenter = new ImageUtility.float2( m_ButtonDownClientCropRectangleCenter.X, m_ButtonDownClientCropRectangleCenter.Y );

                        ImageUtility.float2		OldCenter = m_ButtonDownCropRectangleCenter;
                        ImageUtility.float2		OldHalfSize = m_ButtonDownCropRectangleHalfSize;
                        ImageUtility.float2		OldAxisX = m_ButtonDownCropRectangleAxisX;
                        ImageUtility.float2		OldAxisY = m_ButtonDownCropRectangleAxisY;

                        ImageUtility.float2[]	OldVertices = new ImageUtility.float2[4] {
                            OldCenter - OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                            OldCenter + OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                            OldCenter - OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                            OldCenter + OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                        };

                        ImageUtility.float2		CurCenter = OldCenter;
                        ImageUtility.float2		CurHalfSize = OldHalfSize;
                        ImageUtility.float2		CurAxisX = OldAxisX;
                        ImageUtility.float2		CurAxisY = OldAxisY;

                        RectangleF	ImageRect = ImageClientRect();	// The image's rectangle in client space

                        switch ( m_CropRectangleManipulatedSpot )
                        {
                            case CROP_RECTANGLE_SPOT.LEFT:
                            case CROP_RECTANGLE_SPOT.RIGHT:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                float	OldDistance2Edge = Center2OldPos.Dot( OldAxisX );
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;
                                float	CurDistance2Edge = Center2CurPos.Dot( OldAxisX );

                                float	Delta = CurDistance2Edge - OldDistance2Edge;	// This is the amount (in client space) we need to move the left/right border
                                float	DeltaUV = Delta / ImageRect.Height;				// This is the same amount in UV space
                                DeltaUV *= 0.5f;	// We're dealing with halves all along

                                // Increase width of that amount
                                CurHalfSize.x = OldHalfSize.x + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT ? -1 : 1) * DeltaUV;

                                // Move center along the X axis
                                if ( m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT )
                                {	// Keep right fixed, move to the left
                                    ImageUtility.float2	Right = OldCenter + OldHalfSize.x * OldAxisX;
                                    ImageUtility.float2	Left = Right - 2.0f * CurHalfSize.x * OldAxisX;
                                    CurCenter = 0.5f * (Right + Left);
                                }
                                else
                                {	// Keep left fixed, move to the right
                                    ImageUtility.float2	Left = OldCenter - OldHalfSize.x * OldAxisX;
                                    ImageUtility.float2	Right = Left + 2.0f * CurHalfSize.x * OldAxisX;
                                    CurCenter = 0.5f * (Right + Left);
                                }
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.TOP:
                            case CROP_RECTANGLE_SPOT.BOTTOM:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                float	OldDistance2Edge = Center2OldPos.Dot( OldAxisY );
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;
                                float	CurDistance2Edge = Center2CurPos.Dot( OldAxisY );

                                float	Delta = CurDistance2Edge - OldDistance2Edge;	// This is the amount (in client space) we need to move the left/right border
                                float	DeltaUV = Delta / ImageRect.Height;				// This is the same amount in UV space
                                DeltaUV *= 0.5f;	// We're dealing with halves all along

                                // Increase height of that amount
                                CurHalfSize.y = OldHalfSize.y + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP ? 1 : -1) * DeltaUV;

                                // Move center along the X axis
                                if ( m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP )
                                {	// Keep bottom fixed, move up
                                    ImageUtility.float2	Bottom = OldCenter - OldHalfSize.y * OldAxisY;
                                    ImageUtility.float2	Top = Bottom + 2.0f * CurHalfSize.y * OldAxisY;
                                    CurCenter = 0.5f * (Bottom + Top);
                                }
                                else
                                {	// Keep top fixed, move down
                                    ImageUtility.float2	Top = OldCenter + OldHalfSize.y * OldAxisY;
                                    ImageUtility.float2	Bottom = Top - 2.0f * CurHalfSize.y * OldAxisY;
                                    CurCenter = 0.5f * (Bottom + Top);
                                }
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT:
                            {
                                ImageUtility.float2	Delta = CurClientPos - OldClientPos;
                                ImageUtility.float2	DeltaUV = (1.0f / ImageRect.Height) * Delta;

                                ImageUtility.float2	Corner = new ImageUtility.float2(), OppositeCorner = new ImageUtility.float2();
                                switch ( m_CropRectangleManipulatedSpot )
                                {
                                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Corner = OldVertices[0]; OppositeCorner = OldVertices[3]; break;		// Keep bottom right fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Corner = OldVertices[1]; OppositeCorner = OldVertices[2]; break;		// Keep bottom left fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Corner = OldVertices[2]; OppositeCorner = OldVertices[1]; break;		// Keep top right fixed
                                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Corner = OldVertices[3]; OppositeCorner = OldVertices[0]; break;	// Keep top left fixed
                                }

                                // Move corner
                                Corner += DeltaUV;

                                // Compute new center
                                CurCenter = new ImageUtility.float2( 0.5f * (Corner.x + OppositeCorner.x), 0.5f * (Corner.y + OppositeCorner.y) );

                                // Compute new size
                                CurHalfSize = new ImageUtility.float2( Math.Abs( (Corner - CurCenter).Dot( OldAxisX ) ), Math.Abs( (Corner - CurCenter).Dot( OldAxisY ) ) );

                                break;
                            }

                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                            case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                            {
                                ImageUtility.float2	Center2OldPos = OldClientPos - OldClientCenter;
                                ImageUtility.float2	Center2CurPos = CurClientPos - OldClientCenter;

                                float	OldAngle = (float) Math.Atan2( -Center2OldPos.y, Center2OldPos.x );
                                float	CurAngle = (float) Math.Atan2( -Center2CurPos.y, Center2CurPos.x );

                                m_CropRectangleRotation = m_ButtonDownCropRectangleRotation + CurAngle - OldAngle;

                                CurAxisX = new ImageUtility.float2( (float) Math.Cos( m_CropRectangleRotation ), -(float) Math.Sin( m_CropRectangleRotation ) );
                                CurAxisY = new ImageUtility.float2( -(float) Math.Sin( m_CropRectangleRotation ), -(float) Math.Cos( m_CropRectangleRotation ) );
                                break;
                            }

                            case CROP_RECTANGLE_SPOT.CENTER:
                            {
                                ImageUtility.float2	Delta = CurClientPos - OldClientPos;
                                ImageUtility.float2	DeltaUV = (1.0f / ImageRect.Height) * Delta;

                                CurCenter = OldCenter + DeltaUV;
                                break;
                            }
                        }

                        CurHalfSize.x = Math.Max( 1e-3f, CurHalfSize.x );
                        CurHalfSize.y = Math.Max( 1e-3f, CurHalfSize.y );

                        // Constrain vertices to the image
                        ImageUtility.float2[]	Vertices = BuildClipVertices( CurCenter, CurHalfSize, CurAxisX, CurAxisY );

                        float	MinX = 0.5f * (1.0f - ImageAspectRatio);
                        float	MaxX = 0.5f * (1.0f + ImageAspectRatio);

                        for ( int i=0; i < 4; i++ )
                        {
                            bool	Rebuild = false;
                            if ( Vertices[i].x < MinX )
                            {
                                Vertices[i].x = MinX;
                                Rebuild = true;
                            }
                            if ( Vertices[i].x > MaxX )
                            {
                                Vertices[i].x = MaxX;
                                Rebuild = true;
                            }
                            if ( Vertices[i].y < 0.0f )
                            {
                                Vertices[i].y = 0.0f;
                                Rebuild = true;
                            }
                            if ( Vertices[i].y > 1.0f )
                            {
                                Vertices[i].y = 1.0f;
                                Rebuild = true;
                            }
                            if ( !Rebuild )
                                continue;

                            ImageUtility.float2	OppositeVertex = Vertices[3-i];	// This one is fixed

                            // Recompute center & half size
                            CurCenter = 0.5f * (OppositeVertex + Vertices[i]);
                            ImageUtility.float2	Delta = Vertices[i] - OppositeVertex;
                            CurHalfSize = 0.5f * new ImageUtility.float2( Math.Abs( Delta.Dot( CurAxisX ) ), Math.Abs( Delta.Dot( CurAxisY ) ) );

                            // Rebuild new vertices
                            Vertices = BuildClipVertices( CurCenter, CurHalfSize, CurAxisX, CurAxisY );
                        }

                        m_CropRectangleCenter = CurCenter;
                        m_CropRectangleHalfSize = CurHalfSize;

                        // The crop rectangle has changed!
                        m_CropRectangleIsDefault = false;

                        // Repaint to update the crop rectangle
                        Invalidate();
                    }
                    break;
                }

                case MANIPULATION_STATE.CALIBRATION_TARGET:
                {	// Take care of calibration manipulation
                    Cursor = Cursors.Cross;
                    switch ( m_CalibrationStage )
                    {
                        case CALIBRATION_STAGE.PICK_CENTER:
                            m_CalibrationCenter = Client2ImageUV_NoSquareAspectRatio( e.Location );
                            Invalidate();
                            break;

                        case CALIBRATION_STAGE.SET_RADIUS:
                            {
                                ImageUtility.float2	Temp = Client2ImageUV_NoSquareAspectRatio( e.Location );
                                m_CalibrationRadius = (float) Math.Sqrt( (Temp.x - m_CalibrationCenter.x)*(Temp.x - m_CalibrationCenter.x) + (Temp.y - m_CalibrationCenter.y)*(Temp.y - m_CalibrationCenter.y) );
                                Invalidate();
                            }
                            break;
                    }
                    break;
                }

                default:
                    Cursor = Cursors.Default;
                    break;
            }
        }
Beispiel #2
0
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            m_MousePositionCurrent = e.Location;
            if (m_MouseButtonsDown == MouseButtons.None)
            {
                m_ButtonDownMousePosition         = e.Location;
                m_ButtonDownCropRectangleCenter   = m_CropRectangleCenter;
                m_ButtonDownCropRectangleHalfSize = m_CropRectangleHalfSize;
                m_ButtonDownCropRectangleRotation = m_CropRectangleRotation;
                m_ButtonDownCropRectangleAxisX    = m_CropRectangleAxisX;
                m_ButtonDownCropRectangleAxisY    = m_CropRectangleAxisY;

                m_ButtonDownClientCropRectangleCenter   = m_ClientCropRectangleCenter;
                m_ButtonDownClientCropRectangleVertices = new PointF[4] {
                    m_ClientCropRectangleVertices[0],
                    m_ClientCropRectangleVertices[1],
                    m_ClientCropRectangleVertices[2],
                    m_ClientCropRectangleVertices[3],
                };
            }

            switch (m_ManipulationState)
            {
            case MANIPULATION_STATE.CROP_RECTANGLE:
            {                           // Take care of crop rectangle manipulation
                if (!m_CropRectangleManipulationStarted)
                {                       // Update the cursor based on the hovered manipulation spot
                    m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.NONE;

                    const float TOLERANCE = 8.0f;

                    PointF P = e.Location;

                    PointF TopLeftCorner2UV     = new PointF(P.X - m_ClientCropRectangleVertices[0].X, P.Y - m_ClientCropRectangleVertices[0].Y);
                    PointF BottomRightCorner2UV = new PointF(P.X - m_ClientCropRectangleVertices[3].X, P.Y - m_ClientCropRectangleVertices[3].Y);

                    float Distance2Left   = -TopLeftCorner2UV.X * m_CropRectangleAxisX.x - TopLeftCorner2UV.Y * m_CropRectangleAxisX.y;
                    float Distance2Top    = TopLeftCorner2UV.X * m_CropRectangleAxisY.x + TopLeftCorner2UV.Y * m_CropRectangleAxisY.y;
                    float Distance2Right  = BottomRightCorner2UV.X * m_CropRectangleAxisX.x + BottomRightCorner2UV.Y * m_CropRectangleAxisX.y;
                    float Distance2Bottom = -BottomRightCorner2UV.X * m_CropRectangleAxisY.x - BottomRightCorner2UV.Y * m_CropRectangleAxisY.y;

                    // Stretch
                    if (Math.Abs(Distance2Left) < TOLERANCE)
                    {
                        if (Math.Abs(Distance2Top) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT;
                        }
                        else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT;
                        }
                        else
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.LEFT;
                        }
                    }
                    else if (Math.Abs(Distance2Right) < TOLERANCE)
                    {
                        if (Math.Abs(Distance2Top) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT;
                        }
                        else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT;
                        }
                        else
                        {
                            m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.RIGHT;
                        }
                    }
                    else if (Math.Abs(Distance2Top) < TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.TOP;
                    }
                    else if (Math.Abs(Distance2Bottom) < TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.BOTTOM;
                    }
                    // Rotate
                    else if (Distance2Top > TOLERANCE && Distance2Right > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT;
                    }
                    else if (Distance2Top > TOLERANCE && Distance2Left > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT;
                    }
                    else if (Distance2Bottom > TOLERANCE && Distance2Right > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT;
                    }
                    else if (Distance2Bottom > TOLERANCE && Distance2Left > TOLERANCE)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT;
                    }
                    else if (Distance2Left < 0.0f && Distance2Right < 0.0f && Distance2Bottom < 0.0f && Distance2Top < 0.0f)
                    {
                        m_CropRectangleManipulatedSpot = CROP_RECTANGLE_SPOT.CENTER;
                    }

                    // Update cursor accordingly
                    switch (m_CropRectangleManipulatedSpot)
                    {
                    case CROP_RECTANGLE_SPOT.NONE: Cursor = Cursors.Default; break;

                    case CROP_RECTANGLE_SPOT.LEFT: Cursor = Cursors.SizeWE; break;

                    case CROP_RECTANGLE_SPOT.RIGHT: Cursor = Cursors.SizeWE; break;

                    case CROP_RECTANGLE_SPOT.TOP: Cursor = Cursors.SizeNS; break;

                    case CROP_RECTANGLE_SPOT.BOTTOM: Cursor = Cursors.SizeNS; break;

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Cursor = Cursors.SizeNWSE; break;

                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Cursor = Cursors.SizeNWSE; break;

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Cursor = Cursors.SizeNESW; break;

                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Cursor = Cursors.SizeNESW; break;

                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                        Cursor = Cursors.Hand;
                        break;

                    case CROP_RECTANGLE_SPOT.CENTER:
                        Cursor = Cursors.Hand;
                        break;
                    }
                }
                else
                {                               // Handle actual manipulation
                    ImageUtility.float2 CurClientPos    = new ImageUtility.float2(e.X, e.Y);
                    ImageUtility.float2 OldClientPos    = new ImageUtility.float2(m_ButtonDownMousePosition.X, m_ButtonDownMousePosition.Y);
                    ImageUtility.float2 OldClientCenter = new ImageUtility.float2(m_ButtonDownClientCropRectangleCenter.X, m_ButtonDownClientCropRectangleCenter.Y);

                    ImageUtility.float2 OldCenter   = m_ButtonDownCropRectangleCenter;
                    ImageUtility.float2 OldHalfSize = m_ButtonDownCropRectangleHalfSize;
                    ImageUtility.float2 OldAxisX    = m_ButtonDownCropRectangleAxisX;
                    ImageUtility.float2 OldAxisY    = m_ButtonDownCropRectangleAxisY;

                    ImageUtility.float2[] OldVertices = new ImageUtility.float2[4] {
                        OldCenter - OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                        OldCenter + OldHalfSize.x * OldAxisX + OldHalfSize.y * OldAxisY,
                        OldCenter - OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                        OldCenter + OldHalfSize.x * OldAxisX - OldHalfSize.y * OldAxisY,
                    };

                    ImageUtility.float2 CurCenter   = OldCenter;
                    ImageUtility.float2 CurHalfSize = OldHalfSize;
                    ImageUtility.float2 CurAxisX    = OldAxisX;
                    ImageUtility.float2 CurAxisY    = OldAxisY;

                    RectangleF ImageRect = ImageClientRect();                                   // The image's rectangle in client space

                    switch (m_CropRectangleManipulatedSpot)
                    {
                    case CROP_RECTANGLE_SPOT.LEFT:
                    case CROP_RECTANGLE_SPOT.RIGHT:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        float OldDistance2Edge            = Center2OldPos.Dot(OldAxisX);
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;
                        float CurDistance2Edge            = Center2CurPos.Dot(OldAxisX);

                        float Delta   = CurDistance2Edge - OldDistance2Edge;            // This is the amount (in client space) we need to move the left/right border
                        float DeltaUV = Delta / ImageRect.Height;                       // This is the same amount in UV space
                        DeltaUV *= 0.5f;                                                // We're dealing with halves all along

                        // Increase width of that amount
                        CurHalfSize.x = OldHalfSize.x + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT ? -1 : 1) * DeltaUV;

                        // Move center along the X axis
                        if (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.LEFT)
                        {                                               // Keep right fixed, move to the left
                            ImageUtility.float2 Right = OldCenter + OldHalfSize.x * OldAxisX;
                            ImageUtility.float2 Left  = Right - 2.0f * CurHalfSize.x * OldAxisX;
                            CurCenter = 0.5f * (Right + Left);
                        }
                        else
                        {                                               // Keep left fixed, move to the right
                            ImageUtility.float2 Left  = OldCenter - OldHalfSize.x * OldAxisX;
                            ImageUtility.float2 Right = Left + 2.0f * CurHalfSize.x * OldAxisX;
                            CurCenter = 0.5f * (Right + Left);
                        }
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.TOP:
                    case CROP_RECTANGLE_SPOT.BOTTOM:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        float OldDistance2Edge            = Center2OldPos.Dot(OldAxisY);
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;
                        float CurDistance2Edge            = Center2CurPos.Dot(OldAxisY);

                        float Delta   = CurDistance2Edge - OldDistance2Edge;            // This is the amount (in client space) we need to move the left/right border
                        float DeltaUV = Delta / ImageRect.Height;                       // This is the same amount in UV space
                        DeltaUV *= 0.5f;                                                // We're dealing with halves all along

                        // Increase height of that amount
                        CurHalfSize.y = OldHalfSize.y + (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP ? 1 : -1) * DeltaUV;

                        // Move center along the X axis
                        if (m_CropRectangleManipulatedSpot == CROP_RECTANGLE_SPOT.TOP)
                        {                                               // Keep bottom fixed, move up
                            ImageUtility.float2 Bottom = OldCenter - OldHalfSize.y * OldAxisY;
                            ImageUtility.float2 Top    = Bottom + 2.0f * CurHalfSize.y * OldAxisY;
                            CurCenter = 0.5f * (Bottom + Top);
                        }
                        else
                        {                                               // Keep top fixed, move down
                            ImageUtility.float2 Top    = OldCenter + OldHalfSize.y * OldAxisY;
                            ImageUtility.float2 Bottom = Top - 2.0f * CurHalfSize.y * OldAxisY;
                            CurCenter = 0.5f * (Bottom + Top);
                        }
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT:
                    {
                        ImageUtility.float2 Delta   = CurClientPos - OldClientPos;
                        ImageUtility.float2 DeltaUV = (1.0f / ImageRect.Height) * Delta;

                        ImageUtility.float2 Corner = new ImageUtility.float2(), OppositeCorner = new ImageUtility.float2();
                        switch (m_CropRectangleManipulatedSpot)
                        {
                        case CROP_RECTANGLE_SPOT.CORNER_TOP_LEFT: Corner = OldVertices[0]; OppositeCorner = OldVertices[3]; break;                                                      // Keep bottom right fixed

                        case CROP_RECTANGLE_SPOT.CORNER_TOP_RIGHT: Corner = OldVertices[1]; OppositeCorner = OldVertices[2]; break;                                                     // Keep bottom left fixed

                        case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_LEFT: Corner = OldVertices[2]; OppositeCorner = OldVertices[1]; break;                                                   // Keep top right fixed

                        case CROP_RECTANGLE_SPOT.CORNER_BOTTOM_RIGHT: Corner = OldVertices[3]; OppositeCorner = OldVertices[0]; break;                                                  // Keep top left fixed
                        }

                        // Move corner
                        Corner += DeltaUV;

                        // Compute new center
                        CurCenter = new ImageUtility.float2(0.5f * (Corner.x + OppositeCorner.x), 0.5f * (Corner.y + OppositeCorner.y));

                        // Compute new size
                        CurHalfSize = new ImageUtility.float2(Math.Abs((Corner - CurCenter).Dot(OldAxisX)), Math.Abs((Corner - CurCenter).Dot(OldAxisY)));

                        break;
                    }

                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_TOP_RIGHT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_LEFT:
                    case CROP_RECTANGLE_SPOT.ROTATE_BOTTOM_RIGHT:
                    {
                        ImageUtility.float2 Center2OldPos = OldClientPos - OldClientCenter;
                        ImageUtility.float2 Center2CurPos = CurClientPos - OldClientCenter;

                        float OldAngle = (float)Math.Atan2(-Center2OldPos.y, Center2OldPos.x);
                        float CurAngle = (float)Math.Atan2(-Center2CurPos.y, Center2CurPos.x);

                        m_CropRectangleRotation = m_ButtonDownCropRectangleRotation + CurAngle - OldAngle;

                        CurAxisX = new ImageUtility.float2((float)Math.Cos(m_CropRectangleRotation), -(float)Math.Sin(m_CropRectangleRotation));
                        CurAxisY = new ImageUtility.float2(-(float)Math.Sin(m_CropRectangleRotation), -(float)Math.Cos(m_CropRectangleRotation));
                        break;
                    }

                    case CROP_RECTANGLE_SPOT.CENTER:
                    {
                        ImageUtility.float2 Delta   = CurClientPos - OldClientPos;
                        ImageUtility.float2 DeltaUV = (1.0f / ImageRect.Height) * Delta;

                        CurCenter = OldCenter + DeltaUV;
                        break;
                    }
                    }

                    CurHalfSize.x = Math.Max(1e-3f, CurHalfSize.x);
                    CurHalfSize.y = Math.Max(1e-3f, CurHalfSize.y);

                    // Constrain vertices to the image
                    ImageUtility.float2[] Vertices = BuildClipVertices(CurCenter, CurHalfSize, CurAxisX, CurAxisY);

                    float MinX = 0.5f * (1.0f - ImageAspectRatio);
                    float MaxX = 0.5f * (1.0f + ImageAspectRatio);

                    for (int i = 0; i < 4; i++)
                    {
                        bool Rebuild = false;
                        if (Vertices[i].x < MinX)
                        {
                            Vertices[i].x = MinX;
                            Rebuild       = true;
                        }
                        if (Vertices[i].x > MaxX)
                        {
                            Vertices[i].x = MaxX;
                            Rebuild       = true;
                        }
                        if (Vertices[i].y < 0.0f)
                        {
                            Vertices[i].y = 0.0f;
                            Rebuild       = true;
                        }
                        if (Vertices[i].y > 1.0f)
                        {
                            Vertices[i].y = 1.0f;
                            Rebuild       = true;
                        }
                        if (!Rebuild)
                        {
                            continue;
                        }

                        ImageUtility.float2 OppositeVertex = Vertices[3 - i];                                   // This one is fixed

                        // Recompute center & half size
                        CurCenter = 0.5f * (OppositeVertex + Vertices[i]);
                        ImageUtility.float2 Delta = Vertices[i] - OppositeVertex;
                        CurHalfSize = 0.5f * new ImageUtility.float2(Math.Abs(Delta.Dot(CurAxisX)), Math.Abs(Delta.Dot(CurAxisY)));

                        // Rebuild new vertices
                        Vertices = BuildClipVertices(CurCenter, CurHalfSize, CurAxisX, CurAxisY);
                    }

                    m_CropRectangleCenter   = CurCenter;
                    m_CropRectangleHalfSize = CurHalfSize;

                    // The crop rectangle has changed!
                    m_CropRectangleIsDefault = false;

                    // Repaint to update the crop rectangle
                    Invalidate();
                }
                break;
            }

            case MANIPULATION_STATE.CALIBRATION_TARGET:
            {                           // Take care of calibration manipulation
                Cursor = Cursors.Cross;
                switch (m_CalibrationStage)
                {
                case CALIBRATION_STAGE.PICK_CENTER:
                    m_CalibrationCenter = Client2ImageUV_NoSquareAspectRatio(e.Location);
                    Invalidate();
                    break;

                case CALIBRATION_STAGE.SET_RADIUS:
                {
                    ImageUtility.float2 Temp = Client2ImageUV_NoSquareAspectRatio(e.Location);
                    m_CalibrationRadius = (float)Math.Sqrt((Temp.x - m_CalibrationCenter.x) * (Temp.x - m_CalibrationCenter.x) + (Temp.y - m_CalibrationCenter.y) * (Temp.y - m_CalibrationCenter.y));
                    Invalidate();
                }
                break;
                }
                break;
            }

            default:
                Cursor = Cursors.Default;
                break;
            }
        }