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; } }
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; } }