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();
}
Related
Basically i have a windows form that user will be able to draw different shapes(e.g square, circle and triangle), user will be able to highlight any of these shapes after drawing and then control that highlighted shape by moving or rotating it, i don't know how to rotate the shape. any one can help, this is my code (only to draw a square)
PS: user need to click twice on the form to draw the shape between those 2 points as shown below also i know i should be using onPaint method but this is the requirements of the task
Thanks
public Square(Point keyPt, Point oppPt) // constructor
{
this.keyPt = keyPt;
this.oppPt = oppPt;
}
// You will need a different draw method for each kind of shape. Note the square is drawn
// from first principles. All other shapes should similarly be drawn from first principles.
// Ideally no C# standard library class or method should be used to create, draw or transform a shape
// and instead should utilse user-developed code.
public void draw(Graphics g, Pen blackPen)
{
// This method draws the square by calculating the positions of the other 2 corners
double xDiff, yDiff, xMid, yMid; // range and mid points of x & y
// calculate ranges and mid points
xDiff = oppPt.X - keyPt.X;
yDiff = oppPt.Y - keyPt.Y;
xMid = (oppPt.X + keyPt.X) / 2;
yMid = (oppPt.Y + keyPt.Y) / 2;
// draw square
g.DrawLine(blackPen, (int)keyPt.X, (int)keyPt.Y, (int)(xMid + yDiff / 2), (int)(yMid - xDiff / 2));
g.DrawLine(blackPen, (int)(xMid + yDiff / 2), (int)(yMid - xDiff / 2), (int)oppPt.X, (int)oppPt.Y);
g.DrawLine(blackPen, (int)oppPt.X, (int)oppPt.Y, (int)(xMid - yDiff / 2), (int)(yMid + xDiff / 2));
g.DrawLine(blackPen, (int)(xMid - yDiff / 2), (int)(yMid + xDiff / 2), (int)keyPt.X, (int)keyPt.Y);
}
public void fillSquare(Graphics g, Brush redBrush)
{
float xDiff = oppPt.X - keyPt.X;
float yDiff = oppPt.Y - keyPt.Y;
float xMid = (oppPt.X + keyPt.X) / 2;
float yMid = (oppPt.Y + keyPt.Y) / 2;
var path = new GraphicsPath();
path.AddLines(new PointF[] {
keyPt,
new PointF(xMid + yDiff/2, yMid-xDiff/2),
oppPt
});
path.AddLines(new PointF[] {
keyPt,
new PointF(xMid - yDiff/2, yMid + xDiff/2),
oppPt
});
path.CloseFigure();
// Fill Triangle
g.FillPath(redBrush, path);
}
}
}
i have tried this method but something is missing i don't know what is it
private void itemRotation(PaintEventArgs e)
{
Pen blackpen = new Pen(Color.Black);
Graphics g = e.Graphics;
Font myFont = new System.Drawing.Font("Helvetica", 9);
Brush blackwriter = new SolidBrush(System.Drawing.Color.Black);
if (rotateItem)
{
for (int i = 0; i < shapes.Count; i++)
{
if (shapes[i].Selected)
{
if (shapes[i].ShapeType == (int)ShapeTypes.Square)
{
PointF center = new PointF(shapes[i].keyPt.X + (shapes[i].oppPt.X / 2.0F), shapes[i].keyPt.Y + (shapes[i].oppPt.Y / 2.0F));
shapes[i].keyPt = new Point(shapes[i].keyPt.X, shapes[i].keyPt.Y);
shapes[i].oppPt = new Point(shapes[i].oppPt.X, shapes[i].oppPt.Y);
Matrix myMatrix = new Matrix();
myMatrix.Rotate(30);
g.Transform = myMatrix;
((Square)shapes[i]).draw(g, blackpen);
g.DrawString("2nd pos", myFont, blackwriter, shapes[i].keyPt.X, shapes[i].oppPt.X);
}
}
}
}
}
Below is an example of how to draw the same shape (a GraphicsPath) into various locations and rotations.
The key here is the following two commands
e.Graphics.TranslateTransform(x, y);
e.Graphics.RotateTransform(-angle);
See results below:
and the code used to generate it:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// This code defines a graphics shape using a GraphicsPath
// and draws multiple copies along a grid and with various
// rotation angle angles.
e.Graphics.SmoothingMode=SmoothingMode.AntiAlias;
var target = sender as PictureBox;
// Step 1 - Define a rectangle 20 by 12 pixels, center at origin.
var gp = new GraphicsPath();
gp.AddLines(new PointF[] {
new PointF(-10, -6),
new PointF( 10, -6),
new PointF( 10, 6),
new PointF(-10, 6) });
gp.CloseFigure();
// Step 2 - Define a 10×9 grid with two loops
float angle = 0;
for (int i = 0; i<9; i++)
{
// divide the control height into 10 divisions
float y = (i+1)*target.Height/10;
for (int j = 0; j<10; j++)
{
// divide the control width into 11 divisions
float x = (j+1)*target.Width/11;
// Save the default transformation state
var state = e.Graphics.Save();
// Traslate the origin to (x,y), each grid point
e.Graphics.TranslateTransform(x, y);
// Rotate shape by an angle (negative = CCW)
e.Graphics.RotateTransform(-angle);
// Draw the shape
e.Graphics.FillPath(Brushes.LightSeaGreen, gp);
e.Graphics.DrawPath(Pens.Black, gp);
// Restore the default transformation state
e.Graphics.Restore(state);
// Increment the angle by one degree.
// The idea is to show all 90 degrees of rotation in a 10×9 grid.
angle++;
}
}
}
I need to draw a Fermat spiral in C#. I did it, but I want my drawing to be filled in PictureBox, no matter how big real size is.
public void DrawSpiral(double delta, double numCycles, int oX, int oY, SpiralType spiralType, Color color, Graphics g)
{
double a = Convert.ToInt32(textBox1.Text);
Pen p = new Pen(color, 1);
double prevX = oX;
double prevY = oY;
double X = oX;
double Y = oY;
double fi = Convert.ToInt32(textBox2.Text);
double radius = 0;
while (fi <= (numCycles * 360))
{
fi += delta;
if (spiralType == SpiralType.FermaPlus)
{
radius = a * Math.Sqrt(fi);
}
else if (spiralType == SpiralType.FermaMinus)
{
radius = -a * Math.Sqrt(fi);
}
prevX = X;
prevY = Y;
X = (radius * Math.Cos(fi / 180 * Math.PI)) + oX;
Y = (radius * Math.Sin(fi / 180 * Math.PI)) + oY;
g.DrawLine(p, (float)prevX, (float)prevY, (float)X, (float)Y);
}
}
private void DrawButton_Click(object sender, EventArgs e)
{
pictureBox1.Refresh();
Graphics g = pictureBox1.CreateGraphics();
DrawSpiral(2, 5, 150, 150, SpiralType.FermaPlus, Color.Blue, g);
DrawSpiral(2, 5, 150, 150, SpiralType.FermaMinus, Color.Red, g);
}
So, what should I do to have my drawing to be full filled in the PictureBox.
Here is one way to do it:
Change the signature of the DrawSpiral to include the ClientSize of the PictureBox instead of some center coordinates:
public void DrawSpiral(double delta, double numCycles, int spiralType,
Color color, Graphics g, Size sz)
Then calculate the center dynamically:
int oX = sz.Width / 2;
int oY = sz.Height / 2;
double prevX = oX;
double prevY = oY;
double X = oX;
double Y = oY;
Next calculate the factor a :
a = sz.Width / 2 / Math.Sqrt( numCycles * 360);
Finally call the method only from the Paint event, passing out the valid Graphics object:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Size sz = pictureBox1.ClientSize;
DrawSpiral(2, 5, SpiralType.FermaPlus, Color.Blue, g, sz);
DrawSpiral(2, 5, SpiralType.FermaMinus, Color.Red, g, sz);
}
Upon resizing the PictureBox it will still fill the area with the same number of loops..:
A few notes:
The quality and performance could be improved by first collecting the data in a List<Point> pointsand then using DrawLines(pen, points.ToArray())
I used just the width when I calculated the factor a. Use Math.Min(sz.Width, sz.Height) to always fit it into a non-square box!
I left your offset calculation in place; but you could instead do a g.TranslateTransform()..
The PictureBox will Invalidate/Refresh itself upon resizing. If you change any parameters do call Invalidate to pick them up!
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);
I'm very new to C#, the aim here is to edit the Time of an analog Clock by dragging it's handles. https://code.msdn.microsoft.com/windowsapps/Analog-Clock-Control-0e8ffcab#content this code has inpired me. I have three simple functions MouseDown, MouseMove and MouseUp but still I can not get Drag to work. Any suggestions please ?
public partial class Form1 : Form
{
#region Construct the clock
public Point Start { get; set; }
public Point End { get; set; }
public Form1()
{
InitializeComponent();
DoubleBuffered = true;
//Create the timer and start it
ClockTimer.Tick += ClockTimer_Tick;
ClockTimer.Enabled = true;
ClockTimer.Interval = 1;
ClockTimer.Start();
Start = p1;
End = p2;
}
#endregion
#region Update the clock
private void ClockTimer_Tick(object sender, EventArgs e)
{
Refresh();
}
private Timer ClockTimer = new Timer();
private Pen circle = new Pen(Color.Black, 2);
private Pen secondHandle = new Pen(Color.Red, 1);
private Pen minHandle = new Pen(Color.Black, 5);
private Pen hrHandle = new Pen(Color.Black, 5);
private Point p1;
private Point p2;
#endregion
#region On paint
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
//Clear the graphics to the back color of the control
pe.Graphics.Clear(BackColor);
//Draw the border of the clock
pe.Graphics.DrawEllipse(circle, 0, 0, 300, 300);
//Find the radius of the control by dividing the width by 2
float radius = (300 / 2);
//Find the origin of the circle by dividing the width and height of the control
PointF origin = new PointF(300 / 2, 300 / 2);
//Draw only if ShowMajorSegments is true;
if (ShowMajorSegments)
{
//Draw the Major segments for the clock
for (float i = 0f; i != 390f; i += 30f)
{
pe.Graphics.DrawLine(Pens.White, PointOnCircle(radius - 1, i, origin), PointOnCircle(radius - 21, i, origin));
}
}
//Draw only if ShowMinorSegments is true
if (ShowMinorSegments)
{
//Draw the minor segments for the control
for (float i = 0f; i != 366f; i += 6f)
{
pe.Graphics.DrawLine(Pens.Black, PointOnCircle(radius, i, origin), PointOnCircle(radius - 10, i, origin));
}
}
//Draw only if ShowSecondHand is true
if (ShowSecondhand)
//Draw the second hand
pe.Graphics.DrawLine(secondHandle, origin, PointOnCircle(radius, DateTime.Now.Second * 6f, origin));
//Draw only if ShowMinuteHand is true
if (ShowMinuteHand)
//Draw the minute hand
pe.Graphics.DrawLine(minHandle, origin, PointOnCircle(radius * 0.75f, DateTime.Now.Minute * 6f, origin));
minHandle.StartCap = LineCap.RoundAnchor;
minHandle.EndCap = LineCap.ArrowAnchor;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//Draw only if ShowHourHand is true
if (ShowHourHand)
//Draw the hour hand
pe.Graphics.DrawLine(hrHandle, origin, PointOnCircle(radius * 0.50f, DateTime.Now.Hour * 30f, origin));
hrHandle.StartCap = LineCap.RoundAnchor;
hrHandle.EndCap = LineCap.ArrowAnchor;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
}
#endregion
#region On size changed
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
//Make sure the control is square
if (Size.Height != Size.Width)
Size = new Size(Size.Width, Size.Width);
//Redraw the control
Refresh();
}
#endregion
#region Point on circle
private PointF PointOnCircle(float radius, float angleInDegrees, PointF origin)
{
//Find the x and y using the parametric equation for a circle
float x = (float)(radius * Math.Cos((angleInDegrees - 90f) * Math.PI / 180F)) + origin.X;
float y = (float)(radius * Math.Sin((angleInDegrees - 90f) * Math.PI / 180F)) + origin.Y;
return new PointF(x, y);
}
#endregion
#region Show Minor Segments
private bool showMinorSegments = true;
public bool ShowMinorSegments
{
get
{
return showMinorSegments;
}
set
{
showMinorSegments = value;
Refresh();
}
}
#endregion
#region Show Major Segments
private bool showMajorSegments = true;
public bool ShowMajorSegments
{
get
{
return showMajorSegments;
}
set
{
showMajorSegments = value;
Refresh();
}
}
#endregion
#region Show Second Hand
private bool showSecondHand = false;
public bool ShowSecondhand
{
get
{
return showSecondHand;
}
set
{
showSecondHand = value;
Refresh();
}
}
#endregion
#region Show Minute Hand
private bool showMinuteHand = true;
public bool ShowMinuteHand
{
get
{
return showMinuteHand;
}
set
{
showMinuteHand = value;
Refresh();
}
}
#endregion
#region Show Hour Hand
private bool showHourHand = true;
public bool ShowHourHand
{
get
{
return showHourHand;
}
set
{
showHourHand = value;
Refresh();
}
}
#endregion
public float slope
{
get
{
return (((float)p2.Y - (float)p1.Y) / ((float)p2.X - (float)p1.X));
}
}
public float YIntercept
{
get
{
return p1.Y - slope * p1.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y - cushion) && temp <= (p.Y + cushion))
{
return true;
}
else
{
return false;
}
}
Point deltaStart;
Point deltaEnd;
bool dragging = false;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left && IsPointOnLine(e.Location, 5))
{
dragging = true;
deltaStart = new Point(p1.X - e.Location.X, p1.Y - e.Location.Y);
deltaEnd = new Point(p2.X - e.Location.X, p2.Y - e.Location.Y);
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (dragging && deltaStart != null && deltaEnd != null)
{
p1 = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
p2 = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
this.Refresh();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
}
}
I give a partial answer about translating a X, Y coordinate to an angle (in degree) based on a circle, where the 0° angle is located at the top.
(Scroll down for a compact solution)
Following the directions of typical GUI coordinates, the absolute 0,0 Point is located top left, positive X values stretch to the right and positive Y values stretch to the bottom.
In order to simplify the math, I use a virtual 0,0 point at the center of the circle, so all coordinates need to be translated to locals before calculation and to globals before actual drawing.
Coordinate overview (imagine the circle around 0; 0):
(0;-1)
(-1; 0) (0; 0) (1; 0)
(0; 1)
Now the task is for any coordinate (X; Y) to find the clock-wise angle between the line (0; 0) - (0; -1) and the line (0; 0) - (X; Y)
The circle can be divided into 4 quarter-circles, each covering a combination of signed (X; Y) values.
Quarter 1 contains the angle values 0° to 90° and is represented by positive X values and negative Y values.
Quarter 2 contains the angle values 90° to 180° and is represented by positive X values and positive Y values.
Quarter 3 contains the angle values 180° to 270° and is represented by negative X values and positive Y values.
Quarter 4 contains the angle values 270° to 360° and is represented by negative X values and negative Y values.
Note that for the corner cases 0°, 90°, 180°, 270°, 360° it doesn't really matter which of the two quarters they are assigned to.
The easiest way to understand such problems is to stick to the normal circle -> read: to normalize the X; Y coordinate to a length of 1. Additionally I go with positive values (it would also work without, but a bit differently in the + and - combinations):
var len = Math.Sqrt(X * X + Y * Y);
var xNorm = Math.Abs(X) / len;
var yNorm = Math.Abs(Y) / len;
Now, the reverse sine / cosine can be used to translate the normalized coordinates back into angle values (there's some redundancy in my calculation for the sake of simplicity and completeness):
var angleFromX = Math.Asin(xNorm) * 180.0 / Math.PI;
var angleFromY = Math.Asin(yNorm) * 180.0 / Math.PI;
Now lets apply the appropriate angle for each of the quarter circle areas
var resultAngle = 0.0;
if (quarter_1)
{
resultAngle = 0 + angleFromX;
// same as
resultAngle = 90 - angleFromY;
}
if (quarter_2)
{
resultAngle = 90 + angleFromY;
// same as
resultAngle = 180 - angleFromX;
}
if (quarter_3)
{
resultAngle = 180 + angleFromX;
// same as
resultAngle = 270 - angleFromY;
}
if (quarter_4)
{
resultAngle = 270 + angleFromY;
// same as
resultAngle = 360 - angleFromX;
}
Ofcourse, the quarter_1 - quarter_4 are pseudo-variables that represent the quarter selection as explained.
A more compact solution can be found by analyzing the different properties of the full solution.
var angleFromYAxis = Math.Asin(Y / Math.Sqrt(X * X + Y * Y)) * 180.0 / Math.PI;
var resultAngle = 0.0;
if (X >= 0)
{
resultAngle = 90 + angleFromYAxis;
}
else
{
resultAngle = 270 - angleFromYAxis;
}
I'm making a custom color picker and I have a hsv color wheel as an image in a picturebox. I'm am trying to make it so while my mouse is held down it stays within the circle. I have tired Cursor.Clip but it only uses rectangles.
In this picture, I drew an thin black ellipse around the wheel.
Size mySize = new Size(348, 348);
Point myloc = new Point(26, 26);
Rectangle rec = new Rectangle();
rec.Size = mySize;
rec.Location = myloc;
Graphics.FromImage(pictureBox1.Image).DrawEllipse(Pens.Black, rec);
this.pictureBox1.Refresh();
Sample solution, not best, but works.
Or course, MouseMove event is not good for your purposes, your should attach WinAPI to global mouse move handling, but it is another task.
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 WinForm
{
public partial class frmMain : Form
{
private Point centerPoint; // circle center point
private double radius; // circle radius (R)
private double radius2; // R^2
/// <summary>
/// form constructor
/// </summary>
public frmMain()
{
InitializeComponent();
this.Size = new Size(640, 480);
this.Load += delegate
{
PictureBox pb = new PictureBox();
int size = Math.Min(this.ClientSize.Width, this.ClientSize.Height) - 20;
pb.Location = new Point((this.ClientSize.Width - size) / 2, (this.ClientSize.Height - size) / 2);
pb.Size = new Size(size, size);
pb.Paint += (s, e) =>
{
var g = e.Graphics;
var rect = new Rectangle(Point.Empty, pb.Size);
g.FillEllipse(Brushes.ForestGreen, rect);
g.DrawEllipse(Pens.Navy, rect);
// base cursor clip
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(Cursor.Position.X - 50, Cursor.Position.Y - 50);
Cursor.Clip = pb.RectangleToScreen(rect);
};
this.Controls.Add(pb);
// setup circle parameters
centerPoint = new Point(pb.Left + pb.Width / 2, pb.Top + pb.Height / 2);
radius = (double)size * 0.5;
radius2 = radius * radius;
// bind to events
pb.MouseMove += Mouse_Move;
this.MouseMove += Mouse_Move;
};
}
private void Mouse_Move(object sender, MouseEventArgs e)
{
// client mouse point
var point = this.PointToClient(((Control)sender).PointToScreen(e.Location));
// circle center point
double cx = centerPoint.X;
double cy = centerPoint.Y;
// circle center offset
double sx = point.X - cx;
double sy = point.Y - cy;
// direction angle
double alpha = Math.Atan2(-sy, sx);
this.Text = string.Format("{0} {1} {2:0.00} rad, {3:0} degrees", sx, sy, alpha, 180.0 * alpha / Math.PI);
// range to center
double range = sx * sx + sy * sy;
if (range > radius2)
{
int px = (int)Math.Round(cx + Math.Cos(alpha) * radius);
int py = (int)Math.Round(cy - Math.Sin(alpha) * radius);
Cursor.Position = this.PointToScreen(new Point(px, py));
}
}
}
}