Rotating an Oval and odd mouse problem in Winforms C# - c#

I'm trying to do something that I thought would be pretty simple: Rotate an oval around its center. So I set up a simple program to try and do that. Eventually, what I'll need to do is click on one portion of the oval, and move the mouse in a direction that will cause the oval to rotate. However, at the moment, all I want to do is right click on a form, have an oval appear with the center where the mouse click occurred, then the rotated oval draw next. And it sort of works. I say sort of because the FIRST time I click, the oval and its rotated oval, appear in exactly the correct spot, with the center of the two ovals right where my mouse pointer is. However, if I click again somewhere else on the form, the oval acts as expected (the normal and rotated oval show up, centered on each other), but the ovals are appearing in a completely random place on the form (meaning not on the e.x and e.y co-ords)
Here are the relevant parts of the code:
//this is declared at the top of the form
float theAngle = 0;
Matrix result = new Matrix();
Point center = new Point(0,0);
//this is declared in the form constructor
ovalGraphic = this.CreateGraphics();
//This is declared in the event handler for the mouseclick
if (e.Button == MouseButtons.Right)
{
ovalGraphic.DrawEllipse(pen2, e.X-50, e.Y-15, 100, 30);
xUp_lbl.Text = e.X.ToString();
yUp_lbl.Text = e.Y.ToString();
center.X = e.X;
center.Y = e.Y;
result.RotateAt(theAngle+=10, center);
ovalGraphic.Transform = result;
ovalGraphic.DrawEllipse(pen3, e.X - 50, e.Y - 15, 100, 30);
}
Can anyone see any reason why the oval is appearing in a random place after the first time I click on the form and move the mouse?

This is not how Windows Forms painting works. The forms decide themselves when they paint. This might happen when a form is resized or moved or when another window on top is removed.
Graphics drawn on a form a volatile, i.e. when the form redraws itself it clears is contents by filling itself with the back color. All drawings are lost at this stage and must be repainted.
You can also trigger redraw by calling Invalidate();
You need a class to store the ellipses:
public class Ellipse
{
public Rectangle Rectangle { get; set; }
public float Angle { get; set; }
public PointF Center => new PointF(
Rectangle.Left + 0.5f * Rectangle.Width,
Rectangle.Top + 0.5f * Rectangle.Height);
}
At the top of the form declare (fields are usually preceded by an underscore):
private readonly List<Ellipse> _ellipses = new List<Ellipse>();
private float _theAngle = 0.0f;
Mouse click:
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right) {
var ellipse = new Ellipse {
Rectangle = new Rectangle(e.X - 50, e.Y - 15, 100, 30),
Angle = _theAngle
};
_ellipses.Add(ellipse);
_theAngle += 30; // Just for test purpose.
Invalidate(); // Redraw!
}
}
Then you must override OnPaint:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
foreach (Ellipse ellipse in _ellipses) {
var matrix = new Matrix();
matrix.RotateAt(ellipse.Angle, ellipse.Center);
e.Graphics.Transform = matrix;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
e.Graphics.DrawEllipse(Pens.Red, ellipse.Rectangle);
}
}
Never create a Graphics object yourself, but use the one provided in the PaintEventArgs e.

You have two very distinct questions. One about drawing the ellipses. I answered it in my previous answer on this page. Here I want to answer how to rotate an ellipse with the mouse.
First, we must detect if the mouse hit an ellipse. Therefore, let's add this method to the Ellipse class from the other answer.
public bool IsHit(Point point)
{
// Let's change the coordinates of the point to let the ellipse
// appear as horizontal and as centered around the origin.
PointF p = RotatePoint(point, Center, -Angle);
PointF center = Center;
p.X -= center.X;
p.Y -= center.Y;
// Let's make the ellipse appear as a circle seen from the point.
p.Y *= (float)Rectangle.Width / Rectangle.Height;
float radius = 0.5f * Rectangle.Width;
// We hit if we are inside an ellipse larger by tolerance
// but not inside one smaller by tolerance.
const float tolerance = 3.0f;
float R = radius + tolerance;
float r = radius - tolerance;
float px2 = p.X * p.X;
float py2 = p.Y * p.Y;
return px2 + py2 <= R * R && px2 + py2 >= r * r;
}
It uses this helper method
// Adapted from this answer https://stackoverflow.com/a/13695630/880990 by Fraser.
private static PointF RotatePoint(Point pointToRotate, PointF centerPoint, double angleInDegrees)
{
double angleInRadians = angleInDegrees * (Math.PI / 180);
double cosTheta = Math.Cos(angleInRadians);
double sinTheta = Math.Sin(angleInRadians);
return new PointF {
X =
(float)
(cosTheta * (pointToRotate.X - centerPoint.X) -
sinTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.X),
Y =
(float)
(sinTheta * (pointToRotate.X - centerPoint.X) +
cosTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.Y)
};
}
We also add this method which tells us at witch angle (with respect to the center of the ellipse) we hit the ellipse:
public double HitAngle(Point point)
{
PointF center = Center;
return Math.Atan2(point.Y - center.Y, point.X - center.X);
}
Now let's go back to the form. We need two more fields at the form level:
private Ellipse _hitEllipse;
private double _hitAngle;
In MouseDown we detect if the mouse touches an ellipse. If it does we initiate the rotation by setting the initial parameters in our two new fields:
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
foreach (Ellipse ellipse in _ellipses) {
if (ellipse.IsHit(e.Location)) {
_hitEllipse = ellipse;
_hitAngle = ellipse.HitAngle(e.Location);
Invalidate();
break;
}
}
}
In MouseMove we perform the rotation:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (_hitEllipse != null) {
double newHitAngle = _hitEllipse.HitAngle(e.Location);
double delta = newHitAngle - _hitAngle;
if (Math.Abs(delta) > 0.0001) {
_hitEllipse.Angle += (float)(delta * 180.0 / Math.PI);
_hitAngle = newHitAngle;
Invalidate();
}
}
}
And finally, in MouseUp we stop rotating:
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
_hitEllipse = null; // Stop rotating the ellipse.
Invalidate();
}
In the existing OpPaint method we draw the ellipse in different colors, depending on whether we are rotating it or not. We add additional calls of Invalidate() in MouseDown and MouseUp to make the change immediate.
In OnPaint let's replace the line with DrawEllipse with these two lines:
Pen pen = ellipse == _hitEllipse ? Pens.Red : Pens.Blue;
e.Graphics.DrawEllipse(pen, ellipse.Rectangle);
We can diminish flickering by calling DoubleBuffered = true; in the form constructor (after InitializeComponent();).

Related

Use graphics.ScaleTransform on Drawing

At the moment I develope a ChartControl and it works just pretty well in my opinion,
but now I'm at a point where it would be nice to have the ability to zoom the drawed signal for better analyzing.
At the moment I calculate the needed points like this:
for (int i = 0; i < PointsCount; i++){
xAxisPoint = xAxisOP.X + i * (xAxisWidth / PointsCount);
yAxisPoint = yAxisHeight * data[i].Point / Divisor;
if(yAxisPoint > yAxisHeight){
yAxisPoint = yAxisHeight;
}
if(yAxisPoint < -yAxisHeight){
yAxisPoint = -yAxisHeight;
}
Points[i] = new PointF(xAxisPoint, yAxisOP.Y + yAxisPoint);
}
if(zoom){
graphics.ScaleTransform(0.2f*ZoomFactor, 0.2f*ZoomFactor);
}
using (Pen plotPen = new Pen(plotColor, 1)){
graphics.DrawLines(plotPen, Points);
}
But the problem is: When it zooms in, the zoom is way too big and is drawn outside the bounds of my control.
Is there a way to specify an area in which it should be Scaled (zoomed)?
For the final question: Is there a way to specify an area in which it should be scaled/zoomed? you need a combination of SetClip, TranslateTransform and ScaleTransform.
Here is an example.
It uses a
target rectangle zoomTgtArea where the zoomed graphics are displayed,
a mouse location zoomOrigin where the zoom origin is,
a float zoomFactor, a positive float.
Initial values:
Rectangle zoomTgtArea = new Rectangle(300, 500, 200, 200);
Point zoomOrigin = Point.Empty; // updated in MouseMove when button is pressed
float zoomFactor = 2f;
The trick to zoom in on only a part of the graphics is to display the graphics twice, once normally and once with the transformations of the Graphics object.
Let's try:
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
// normal drawing
DrawStuff(e.Graphics);
// for the movable zoom we want a small correction
Rectangle cr = pictureBox.ClientRectangle;
float pcw = cr.Width / (cr.Width - ZoomTgtArea.Width / 2f) ;
float pch = cr.Height / (cr.Height - ZoomTgtArea.Height / 2f) ;
// now we prepare the graphics object; note: order matters!
e.Graphics.SetClip(zoomTgtArea );
// we can either follow the mouse or keep the output area fixed:
if (cbx_fixed.Checked)
e.Graphics.TranslateTransform( ZoomTgtArea.X - zoomCenter.X * zoomFactor,
ZoomTgtArea.Y - zoomCenter.Y * zoomFactor);
else
e.Graphics.TranslateTransform( - zoomCenter.X * zoomFactor * pcw,
- zoomCenter.Y * zoomFactor * pch);
// finally zoom
e.Graphics.ScaleTransform(zoomFactor, zoomFactor);
// and display zoomed
DrawStuff(e.Graphics);
}
The DrawStuff I used is simple:
void DrawStuff(Graphics g)
{
bool isZoomed = g.Transform.Elements[0]!= 1
|| g.Transform.OffsetX != 0 | g.Transform.OffsetY != 0;
if (isZoomed) g.Clear(Color.Gainsboro); // pick your back color
// all your drawing here!
Rectangle r = new Rectangle(10, 10, 500, 800); // some size
using (Font f = new Font("Tahoma", 11f))
g.DrawString(text, f, Brushes.DarkSlateBlue, r);
}
Its only extra is clearing the background so the normal drawing won't shine through the zoomed version..
Let's see:

C# WPF MouseLeftButtonUp event not firing when also using MouseMove

I have the following XAML code in my WPF test application:
<Canvas x:Name="canvas"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="CornflowerBlue" MouseMove="canvas_MouseMove"
MouseLeftButtonUp="canvas_MouseLeftButtonUp"
/>
And in my .cs file:
private const int Radius = 10;
private Ellipse _prev;
private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (this._prev != null)
canvas.Children.Remove(this._prev);
var pos = e.GetPosition(canvas);
var c = new Ellipse()
{
Height = 2 * Radius,
Width = 2 * Radius,
Fill = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)),
};
double x = pos.X - Radius;
double y = pos.Y - Radius;
Canvas.SetTop(c, y);
Canvas.SetLeft(c, x);
canvas.Children.Add(c);
}
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed)
{
if (this._prev != null)
canvas.Children.Remove(this._prev);
var pos = e.GetPosition(canvas);
_prev = new Ellipse()
{
Height = 2 * Radius,
Width = 2 * Radius,
Fill = new SolidColorBrush(Color.FromArgb(100, 255, 0, 0)),
};
double x = pos.X - Radius;
double y = pos.Y - Radius;
Canvas.SetTop(_prev, y);
Canvas.SetLeft(_prev, x);
canvas.Children.Add(_prev);
}
}
What I want to do is basically when a user click the left mouse button and drags around, a red circle with a little bit of alpha follows the cursor. When the user releases the mouse, the red circle stays there with no more alpha coloring. Seems like a pretty simple task, however the MouseLeftButtonUp event only fires when I don't drag the mouse around prior to releasing it. What am I doing wrong here?
you are always removing and adding ellipse to canvas in canvas_MouseMove function. if you try changing the position of ellipse in canvas_MouseMove you can do what you want

Rotating a square without messing the x and y [duplicate]

I am working on a project for school, we need to make a basic top down race game in C# without using XNA.
First of all let me tell you that the stuff we have learned about programming so far has little to do with making something that even remotely looks like a racegame. It didn't get any more difficult than array's, loops etc.
So we didn't learn about graphics or anything like that.
Having said all that I am having the following problem.
We have created a Graphics object, and then use DrawImage and use a bitmap from a car.jpg.
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);
Then we wait for a key press e.g Right
case Keys.Right:
if (angle != 360)
{
angle += 10;
}
else
{
angle = 0;
}
this.Refresh();
break;
The problem we have is that the pivot point for the rotation is in the top left corner. So as soon as we move the car to something like (20,25) and start to rotate it, it will use (0,0) as the center of rotation. What we want to achieve is to have the center point of rotation at the center of our car.
We have tried looking for ways to change the centerX and centerY of the RotateTransform but have come to the conclusion that this isn't possible with the bitmap.
We have been struggling with this problem for over 2 days and can't seem to find any solution for achieving the thing we want.
Is there something we are doing wrong creating the Graphics object, or is there a totally different way to change centerX and centerY for the car?
To draw a rotated Bitmap you need to do a few steps to prepare the Graphics object:
first you move its origin onto the midpoint of the rotation
then you rotate by the desired angle
next you move it back
now you can draw the Bitmap
finally you reset the Graphics
This needs to be done for each bitmap.
Here are the steps in code to draw a Bitmap bmp at position (xPos, yPos):
float moveX = bmp.Width / 2f + xPos;
float moveY = bmp.Height / 2f+ xPosf;
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);
e.Graphics.ResetTransform();
There is one possible complication: If your Bitmap has different dpi resolution than the screen i.e. than the Graphics you must first adapt the Bitmap's dpi setting!
To adapt the Bitmapto the usual 96dpi you can simply do a
bmp.SetResolution(96,96);
To be prepared for future retina-like displays you can create a class variable you set at startup:
int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)
{
using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;
}
and use it after loading the Bitmap:
bmp.SetResolution(ScreenDpi , ScreenDpi );
As usual the DrawImage method uses the top left corner of the Bitmap. You may need to use different Points for the rotation point and possibly also for the virtual position of your car, maybe in the middle of its front..
Here is static class which will paint the image in desired location within desired area. Change the rotationangle value to rotate the image. And you can also pan and zoom the image.
Add this class in your Project and call the static functions from Win Form.
public static class FullImage
{
public static Image image;
public static RectangleF DisplayRect, SourceRect;
public static Size ParentBoundry;
public static float rotationangle=0;
internal static void Paint(Graphics graphics)
{
if (image == null)
return;
float hw = DisplayRect.X + DisplayRect.Width / 2f;
float hh = DisplayRect.Y + DisplayRect.Height / 2f;
System.Drawing.Drawing2D.Matrix m = graphics.Transform;
m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
graphics.Transform = m;
graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
graphics.ResetTransform();
}
public static void LoadImage(Image img)
{
image = img;
SizeF s = GetResizedSize(image, ParentBoundry);
SourceRect = new RectangleF(0, 0, image.Width, image.Height);
DisplayRect = new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
}
public static Size GetResizedSize(Image ImageToResize, Size size)
{
int sourceWidth = ImageToResize.Width;
int sourceHeight = ImageToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
return new Size(destWidth, destHeight);
}
internal static void MouseWheel(int delta)
{
if (delta > 0)
DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
else
DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);
}
private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
{
/// Original Size and Location
SizeF OriginalSize = ImageRectangle.Size;
PointF OriginalPoint = ImageRectangle.Location;
///Mouse cursor location -located in width% and height% of totaloriginal image
float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;
///Zoomed Image by scalefactor
SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
if (FinalSize.Width < 15 || FinalSize.Height < 15)
return ImageRectangle;
if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
return ImageRectangle;
/// How much width increases and height increases
float widhtincrease = FinalSize.Width - OriginalSize.Width;
float heightincrease = FinalSize.Height - OriginalSize.Height;
/// Adjusting Image location after zooming the image
PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);
return ImageRectangle;
}
static bool drag = false;
static Point Initial, CurrentMouse;
internal static void MouseMove(Point location)
{
CurrentMouse = location;
if (drag)
{
DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
Initial = location;
}
}
internal static void MouseDown(Point location)
{
Initial = location;
drag = true;
}
internal static void MouseUp(Point location)
{
drag = false;
}
}
After Adding this code in your project (Better add in separate cs file), Call the functions from Win Form class (Form1.cs).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
FullImage.ParentBoundry = new Size(this.Width, this.Height);
// Enter the image path
FullImage.LoadImage(Image.FromFile(#"D:\a.jpg"));
}
//Create a paint event
private void Form1_Paint(object sender, PaintEventArgs e)
{
FullImage.Paint(e.Graphics);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseDown(e.Location);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseMove(e.Location);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseUp(e.Location);
this.Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Vault.FullImage.MouseWheel(e.Delta);
this.Invalidate();
}
Now, if you want to rotate the image, just set the value however you want (with slider, button or add some more functions to detect the mouse movement and then rotate)
Example: add a button and each time the button clicked increase the value by 1.
private void button1_clicked(object sender, EventArgs e)
{
FullImage.rotationangle++;
this.invalidate();
}
To rotate the top left from the center you first need to know the angle of it then adjust it by the angle you want and re-calculate the new top left by the new angle:
var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);

Making eyeball look at/follow cursor movement (C#)

I'm stuck here. I want to have the eyes drawn while 'looking' at (the angle of) the cursor. Also, it should be contained within the bigger circle/quadrant (just like an eyeball). Sadly, it just won't draw the eye for me at the right position/angle and at every mouse movement. The only thing it will do is initially draw an ellipse at (0,0), but that's not what I want.
My idea is to calculate the ratio of the triangles with pythagorean theorem. Then apply the right coordinates (With the correct ratio) in the drawEllipse(); method. This should be repeated everytime you move the cursor.
You can check my image for the mathematical reasoning.
Here is my code, note that the panel is made in the designer mode which isn't included in this code, but shouldn't be a big deal:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace oogjes2
{
public partial class Form1 : Form
{
public int mousex;
public int mousey;
public Form1()
{
InitializeComponent();
panel1.Paint += paintpanel;
panel1.MouseMove += panel1_MouseMove;
}
//panel1 cover the screen from (0.0) and onwards,
void panel1_MouseMove(object sender, MouseEventArgs mea)
{
int mousex = mea.X;
int mousey = mea.Y;
}
void paintpanel(object obj, PaintEventArgs pea)
{
Pen blackpen = new Pen(Color.Black, 3);
// the black outer circle which doesnt move
pea.Graphics.DrawEllipse(blackpen, -125, -125, 250, 250);
// e = 63. Diagonal distance from (0,0) to starting point drawEllipse
// factor = multiplication from mea.x and mea.y to the respective ex and ey of the small circle.
// ey = factor * mousex (mea.X). Same for ex.
float e = (float)Math.Sqrt(45 * 45 + 45 * 45); //=63
float factor = (e / (float)Math.Sqrt(mousex * mousex + mousey * mousey));
int ex = mousex * (int)factor;
int ey = mousey * (int)factor;
// the eye that should be redrawn at every mousemovement
pea.Graphics.DrawEllipse(blackpen, ex, ey, 50, 50);
this.Invalidate();
}
}
}
Try this out:
public partial class Form1 : Form
{
private Timer tmr;
private int PupilRadius = 20;
private int EyeBallRadius = 50;
private int DistanceBetweenEyes = 20;
public Form1()
{
InitializeComponent();
this.panel1.Paint += panel1_Paint;
tmr = new Timer();
tmr.Interval = 100;
tmr.Tick += tmr_Tick;
tmr.Start();
}
void tmr_Tick(object sender, EventArgs e)
{
panel1.Invalidate();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
Point center = new Point(panel1.ClientSize.Width / 2, panel1.ClientSize.Height / 2);
Point LeftEyeCenter = new Point(center.X - EyeBallRadius - (DistanceBetweenEyes / 2), center.Y);
Point RightEyeCenter = new Point(center.X + EyeBallRadius + (DistanceBetweenEyes / 2), center.Y);
Rectangle rc = new Rectangle(LeftEyeCenter, new Size(1, 1));
rc.Inflate(EyeBallRadius, EyeBallRadius);
e.Graphics.DrawEllipse(Pens.Black, rc);
rc = new Rectangle(RightEyeCenter, new Size(1, 1));
rc.Inflate(EyeBallRadius, EyeBallRadius);
e.Graphics.DrawEllipse(Pens.Black, rc);
Point curPos = panel1.PointToClient(Cursor.Position);
Double DistanceFromLeftEyeToCursor = getDistance(LeftEyeCenter.X, LeftEyeCenter.Y, curPos.X, curPos.Y);
Double DistanceFromRightEyeToCursor = getDistance(RightEyeCenter.X, RightEyeCenter.Y, curPos.X, curPos.Y);
double angleLeft = getAngleInDegrees(LeftEyeCenter.X, LeftEyeCenter.Y, curPos.X, curPos.Y);
double angleRight = getAngleInDegrees(RightEyeCenter.X, RightEyeCenter.Y, curPos.X, curPos.Y);
rc = new Rectangle(new Point(Math.Min((int)DistanceFromLeftEyeToCursor, EyeBallRadius - PupilRadius), 0), new Size(1, 1));
rc.Inflate(PupilRadius, PupilRadius);
e.Graphics.TranslateTransform(LeftEyeCenter.X, LeftEyeCenter.Y);
e.Graphics.RotateTransform((float)angleLeft);
e.Graphics.FillEllipse(Brushes.Blue, rc);
rc = new Rectangle(new Point(Math.Min((int)DistanceFromRightEyeToCursor, EyeBallRadius - PupilRadius), 0), new Size(1, 1));
rc.Inflate(PupilRadius, PupilRadius);
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(RightEyeCenter.X, RightEyeCenter.Y);
e.Graphics.RotateTransform((float)angleRight);
e.Graphics.FillEllipse(Brushes.Blue, rc);
}
private Double getDistance(int Ax, int Ay, int Bx, int By)
{
return Math.Sqrt(Math.Pow((Double)Ax - Bx, 2) + Math.Pow((Double)Ay - By, 2));
}
private Double getAngleInDegrees(int cx, int cy, int X, int Y)
{
// draw a line from the center of the circle
// to the where the cursor is...
// If the line points:
// up = 0 degrees
// right = 90 degrees
// down = 180 degrees
// left = 270 degrees
Double angle;
int dy = Y - cy;
int dx = X - cx;
if (dx == 0) // Straight up and down | avoid divide by zero error!
{
if (dy <= 0)
{
angle = 0;
}
else
{
angle = 180;
}
}
else
{
angle = Math.Atan((Double)dy / (Double)dx);
angle = angle * ((Double)180 / Math.PI);
if (X <= cx)
{
angle = 180 + angle;
}
}
return angle;
}
}
If you want to have an eye following the cursor, you'll need to calculate the angle from the eye to the cursor.
You'll need to know just three things:
the position of the eye, the position of the mouse, and how far the center of the pupil is from the center of the eye (I'm calling your inner circle the pupil and outer circle the eye).
Since the eye never moves (only rotates around it's center) you already know it's position.
void direction_to_cursor(){
float p = Math.sqrt((45 + r)*(45 + r)*2); // Distance from outer circle center to inner circle center
// assuming you want the top left corner 63 away from 0, 0
// r is radius of inner circle
int x = mouseX - EyeX; // In your picture it looks like your eye is at 0,0
int y = -(mouseY - EyeY); // inverted y axis (0 is at top)
float dir = Math.atan2(x, y);
int px = p * Math.cos(dir); // x Center of inner circle
int py = p * Math.cos(dir); // y Center of inner circle
px -= r; // Get left x coordinate of circle
py -= r; // get right x coordinate of circle
pea.Graphics.DrawEllipse(blackpen, px, py, 50, 50);
}
step1: Calculate distance from center of Eye to center of pupil
step2: Calculate x and y difference between the Mouse and Eye
step3: Calculate direction from eye to mouse.
step4: Calculate position of pupil from direction and distance from center of eye
you could use the following
void paintpanel(object obj, PaintEventArgs pea)
{
Pen blackpen = new Pen(Color.Black, 3);
pea.Graphics.DrawEllipse(blackpen, -125, -125, 250, 250);
float p = Math.sqrt(2*70*70); // (45+25)*(45+25)+(45+25)*(45+25)
float dir = Math.atan(y, x);
int ex = Math.cos(dir) * p - 25;
int ey = Math.sin(dir) * p - 25;
// the eye that should be redrawn at every mousemovement
pea.Graphics.DrawEllipse(blackpen, ex, ey, 50, 50);
this.Invalidate();
}

Getting the mouse position on a zoomed picture

I am creating a screenshot maker/uploader and everything works great but now i wanna intergrate something like paint. So first i am creating the pencil function. But there starts the problems i can draw :D but not on the position of my mouse. He takes other position then my cursor?
So the question is:
Hoe to get the mouse positions on a zoomed picturebox?
My code:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (draw && pencil)
{
Graphics panel = Graphics.FromImage(ScreenShot);
Pen pen = new Pen(Color.Black, 14);
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round;
panel.DrawLine(pen, pX, pY, e.X, e.Y);
pictureBox1.CreateGraphics().DrawImage(ScreenShot, pictureBox1.Width, pictureBox1.Height);
pictureBox1.Invalidate();
}
Point p = pictureBox1.PointToClient(Cursor.Position);
pX = p.X;
pY = p.Y;
}
When using the mouse on a zoomed image the reported pixels are the zoomed coordinates. So we need to get at the real pixels..
After setting up things somehow..
float pbZoom = 3f; // the factor by which the PictureBox is zoomed in or out
pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
pictureBox.ClientSize = new Size((int)(pictureBox.Image.Width * pbZoom),
(int)(pictureBox.Image.Height * pbZoom));
pictureBox.Paint += pictureBox_Paint;
pictureBox.MouseDown += pictureBox_MouseDown;
pictureBox.MouseMove += pictureBox_MouseMove;
pictureBox.MouseUp += pictureBox_MouseUp;
.. you can write the events to draw onto or into the zoomed image.
First a helper function to undo the zoom for a point:
PointF unZoomed(PointF pt) { return new PointF(pt.X / pbZoom, pt.Y / pbZoom );}
For simplicity lets keep a few things at class level:
PointF mDown = PointF.Empty; // mouse down point
PointF mCurrent = PointF.Empty; // current mouse location
We start each mouse event by calculating the unzoomed e.Location.
Our test draws a straight, moving red line on the surface until you release the mouse. Then that line is drawn into the Image in green.
The MouseDown simply stores the starting point, unzoomed:
void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
mDown = unZoomed(e.Location);
}
In the MouseMove we store the current point and trigger the Paint event:
void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.None) return;
PointF e_ = unZoomed(e.Location);
mCurrent = e_;
pictureBox.Invalidate();
}
In the MouseUp we draw the line into the Image of the PictureBox, changing its pixels; then we set the Image and reset the points:
void pictureBox_MouseUp(object sender, MouseEventArgs e)
{
Bitmap bmp = (Bitmap) pictureBox.Image;
using (Graphics G = Graphics.FromImage(bmp))
{
G.SmoothingMode = SmoothingMode.HighQuality;
G.DrawLine(Pens.Green, mDown, mCurrent);
}
mDown = PointF.Empty;
mCurrent = PointF.Empty;
pictureBox.Image = bmp;
}
To draw onto the Control surface we use the Paint event. To draw scaled we use a Matrix..:
void pictureBox_Paint(object sender, PaintEventArgs e)
{
Matrix scaleMatrix = new Matrix();
scaleMatrix.Scale(pbZoom, pbZoom);
e.Graphics.MultiplyTransform(scaleMatrix);
e.Graphics.DrawLine(Pens.Red, mDown, mCurrent);
}
I also working with zoom image. I simply use this code, it's working. So you can just compare with bitmap (original image) before zoomed in/out.
private void pbInput_MouseMove(object sender, MouseEventArgs e) {
if (pbInput.Image == null) {
mouseY.Text = "";
mouseX.Text = "";
}
else {
Bitmap b = new Bitmap(pbInput.Image);
int x = b.Width * e.X / pbInput.Width;
int y = b.Height * e.Y / pbInput.Height;
mouseX.Text = x.ToString();
mouseY.Text = y.ToString();
}
}
this.Cursor = new Cursor(Cursor.Current.Handle);
Size size1 = pictureBox1.Image.Size;
Size size2 = pictureBox1.Size;
float x1 = (float)size1.Width / ((float)size2.Width + 1);
float y1 = (float)size1.Height / ((float)size2.Height + 1);
float divisor = x1 >= y1 ? x1 : y1;
divisor = divisor > 0 ? divisor : 1;
Size imagesize = new Size((int)(size1.Width / divisor), (int)(size1.Height / divisor));
int ex = imagesize.Width - size2.Width >= 0 ? imagesize.Width - size2.Width : size2.Width - imagesize.Width;
int ey = imagesize.Height - size2.Height >= 0 ? imagesize.Height - size2.Height : size2.Height - imagesize.Height;
float ftx = ((float)size1.Width / (float)imagesize.Width) * ((float)(Cursor.Position.X - this.Location.X - pictureBox1.Location.X - (ex / 2) - 8));
float fty = ((float)size1.Height / (float)imagesize.Height) * ((float)(Cursor.Position.Y - this.Location.Y - pictureBox1.Location.Y - (ey / 2) - 31));
Point LOP = new Point(Convert.ToInt32(ftx), Convert.ToInt32(fty)); //point of cursos on picturebox image pixel ...

Categories

Resources