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!
Related
I have next task - draw ellipe trough 3 points (like in the picture).
.
User can drag theese points that to change ellipse size. Two of points placed on edges of great axis. In my solution I use GDI throug Graphics class.
Now my solution is calculate rectangle corresponding to an ellipse and use
DrawEllipse method and rotate transform (if it's need). But this solution has many mathematical calculation. Maybe there is the simplest approach to solve this task?
Here is how to use the DrawEllipse method from a rotation, the minor axis and two vertices.
First we calculate the Size of the bounding Rectangle:
Given the Points A and B sitting on the short sides of length smallSize we get the long side with a little Pythagoras:
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
So :
Size size = new System.Drawing.Size(longSide, smallSize);
Next we need the rotation angle:
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
And it will make things easier to also get the center Point C:
Point C = new Point((A.X + B.X)/ 2, (A.Y + B.Y)/ 2);
The last thing we want is a routine that draws an ellipse of a given Size, rotated around C at an angle:
void DrawEllipse(Graphics G, Pen pen, Point center, Size size, float angle)
{
int h2 = size.Height / 2;
int w2 = size.Width / 2;
Rectangle rect = new Rectangle( new Point(center.X - w2, center.Y - h2), size );
G.TranslateTransform(center.X, center.Y);
G.RotateTransform(angle);
G.TranslateTransform(-center.X, -center.Y);
G.DrawEllipse(pen, rect);
G.ResetTransform();
}
[![enter image description here][1]][1]
Here is a little testbed that brings it all together:
Point A = new Point(200, 200); // *
Point B = new Point(500, 250);
int smallSize = 50;
void doTheDraw(PictureBox pb)
{
Bitmap bmp = new Bitmap(pb.Width, pb.Height);
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
Point C = new Point((A.X + B.X) / 2, (A.Y + B.Y) / 2);
Size size = new System.Drawing.Size((int)longSide, smallSize);
using (Pen pen = new Pen(Color.Orange, 3f))
using (Graphics g = Graphics.FromImage(bmp))
{
// a nice background grid (optional):
DrawGrid(g, 0, 0, 100, 50, 10,
Color.LightSlateGray, Color.DarkGray, Color.Gainsboro);
// show the points we use (optional):
g.FillEllipse(Brushes.Red, A.X - 4, A.Y - 4, 8, 8);
g.FillRectangle(Brushes.Red, B.X - 3, B.Y - 3, 7, 7);
g.FillEllipse(Brushes.Red, C.X - 5, C.Y - 5, 11, 11);
// show the connection line (optional):
g.DrawLine(Pens.Orange, A, B);
// here comes the ellipse:
DrawEllipse(g, pen, C, size, angle);
}
pb.Image = bmp;
}
The grid is a nice helper:
void DrawGrid(Graphics G, int ox, int oy,
int major, int medium, int minor, Color c1, Color c2, Color c3)
{
using (Pen pen1 = new Pen(c1, 1f))
using (Pen pen2 = new Pen(c2, 1f))
using (Pen pen3 = new Pen(c3, 1f))
{
pen2.DashStyle = DashStyle.Dash;
pen3.DashStyle = DashStyle.Dot;
for (int x = ox; x < G.VisibleClipBounds.Width; x += major)
G.DrawLine(pen1, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += major)
G.DrawLine(pen1, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += medium)
G.DrawLine(pen2, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += medium)
G.DrawLine(pen2, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += minor)
G.DrawLine(pen3, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += minor)
G.DrawLine(pen3, 0, y, G.VisibleClipBounds.Width, y);
}
}
Note that I made A, B, smallSide class level variables so I can modify them during my tests, (and I did *)..
As you can see I have added a TrackBar to make the smallside dynamic; for even more fun I have added this MouseClick event:
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left)) A = e.Location;
else B = e.Location;
doTheDraw(pictureBox1);
}
I hope some of my logic can help you out on what you're trying to achieve. Next question you make try to add more info and be less generic.
Compute the similarity transform (translation and rotation) that brings the first two points to (-a, 0) and (a, 0). [This can be done with a matrix transform or complex numbers.]
Apply the same transform to the third point. Now the equation of the reduced ellipse is
x² / a² + y² / b² = 1.
You simply determine the parameter b by plugging the reduced coordinates of the third point,
b = y / √(1 - x²/a²).
Now you have
the center,
the orientation of the axis,
the length of the major axis,
the length of the minor axis.
I have a C# bitmap object, and i am able to draw a line from point A to point B.
I have the 2 points on the edges of the diagram, and I would like to draw an ellipse from A to B. The basic g.DrawEllipse() only draws ellipses either perfectly horizontally or vertically, however I need the ellipse to be kind of diagonal from the one end of the image to the other.
My bitmap: 200 tall by 500 wide
Point A: Column 0, Row 20 (0,20)
Point B: Column 499, Row 60 (499, 60)
Widest Point: 30 - Narrow Radius of the ellipse
Here is what I have so far, the draw ellipse doesnt have the overload I need, so help there please:
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawLine(pen, new Point(20,0), new Point(499,60));
g.DrawEllipse(pen, 20, 0, someWidth, someHeight);
}
Here is how to use the DrawEllipse method from a rotation, the minor axis and two vertices.
First we calculate the Size of the bounding Rectangle:
Given the Points A and B sitting on the short sides of length smallSize we get the long side with a little Pythagoras:
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
So :
Size size = new System.Drawing.Size(longSide, smallSize);
Next we need the rotation angle:
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
And it will make things easier to also get the center Point C:
Point C = new Point((A.X + B.X)/ 2, (A.Y + B.Y)/ 2);
The last thing we want is a routine that draws an ellipse of a given Size, rotated around C at an angle:
void DrawEllipse(Graphics G, Pen pen, Point center, Size size, float angle)
{
int h2 = size.Height / 2;
int w2 = size.Width / 2;
Rectangle rect = new Rectangle( new Point(center.X - w2, center.Y - h2), size );
G.TranslateTransform(center.X, center.Y);
G.RotateTransform(angle);
G.TranslateTransform(-center.X, -center.Y);
G.DrawEllipse(pen, rect);
G.ResetTransform();
}
Here is a little testbed that brings it all together:
Point A = new Point(200, 200); // *
Point B = new Point(500, 250);
int smallSize = 50;
void doTheDraw(PictureBox pb)
{
Bitmap bmp = new Bitmap(pb.Width, pb.Height);
float angle = -(float)(Math.Atan2(A.Y - B.Y, B.X - A.X) * 180f / Math.PI);
int longSide = (int)(Math.Sqrt((A.Y - B.Y) * (A.Y - B.Y) + (B.X - A.X) * (B.X - A.X)));
Point C = new Point((A.X + B.X) / 2, (A.Y + B.Y) / 2);
Size size = new System.Drawing.Size((int)longSide, smallSize);
using (Pen pen = new Pen(Color.Orange, 3f))
using (Graphics g = Graphics.FromImage(bmp))
{
// a nice background grid (optional):
DrawGrid(g, 0, 0, 100, 50, 10,
Color.LightSlateGray, Color.DarkGray, Color.Gainsboro);
// show the points we use (optional):
g.FillEllipse(Brushes.Red, A.X - 4, A.Y - 4, 8, 8);
g.FillRectangle(Brushes.Red, B.X - 3, B.Y - 3, 7, 7);
g.FillEllipse(Brushes.Red, C.X - 5, C.Y - 5, 11, 11);
// show the connection line (optional):
g.DrawLine(Pens.Orange, A, B);
// here comes the ellipse:
DrawEllipse(g, pen, C, size, angle);
}
pb.Image = bmp;
}
The grid is a nice helper:
void DrawGrid(Graphics G, int ox, int oy,
int major, int medium, int minor, Color c1, Color c2, Color c3)
{
using (Pen pen1 = new Pen(c1, 1f))
using (Pen pen2 = new Pen(c2, 1f))
using (Pen pen3 = new Pen(c3, 1f))
{
pen2.DashStyle = DashStyle.Dash;
pen3.DashStyle = DashStyle.Dot;
for (int x = ox; x < G.VisibleClipBounds.Width; x += major)
G.DrawLine(pen1, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += major)
G.DrawLine(pen1, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += medium)
G.DrawLine(pen2, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += medium)
G.DrawLine(pen2, 0, y, G.VisibleClipBounds.Width, y);
for (int x = ox; x < G.VisibleClipBounds.Width; x += minor)
G.DrawLine(pen3, x, 0, x, G.VisibleClipBounds.Height);
for (int y = oy; y < G.VisibleClipBounds.Height; y += minor)
G.DrawLine(pen3, 0, y, G.VisibleClipBounds.Width, y);
}
}
Note that I made A, B, smallSide class level variables so I can modify them during my tests, (and I did *)..
As you can see I have added a TrackBar to make the smallside dynamic; for even more fun I have added this MouseClick event:
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left)) A = e.Location;
else B = e.Location;
doTheDraw(pictureBox1);
}
Note that I didn't care for disposing of the old Bitmap; you should, of course..!
If you wish to use Graphics to create a diagonal ellipse, perhaps you can use DrawBezier() method.
Here is some code that does it:
// Draws an ellipse using 2 beziers.
private void DrawEllipse(Graphics g, PointF center, float width, float height, double rotation)
{
// Unrotated ellipse frame
float left = center.X - width / 2;
float right = center.X + width / 2;
float top = center.Y - height / 2;
float bottom = center.Y + height / 2;
PointF p1 = new PointF(left, center.Y);
PointF p2 = new PointF(left, top);
PointF p3 = new PointF(right, top);
PointF p4 = new PointF(right, center.Y);
PointF p5 = new PointF(right, bottom);
PointF p6 = new PointF(left, bottom);
// Draw ellipse with rotated points.
g.DrawBezier(Pens.Black, Rotate(p1, center, rotation), Rotate(p2, center, rotation), Rotate(p3, center, rotation), Rotate(p4, center, rotation));
g.DrawBezier(Pens.Black, Rotate(p4, center, rotation), Rotate(p5, center, rotation), Rotate(p6, center, rotation), Rotate(p1, center, rotation));
}
// Rotating a given point by given angel around a given pivot.
private PointF Rotate(PointF point, PointF pivot, double angle)
{
float x = point.X - pivot.X;
float y = point.Y - pivot.Y;
double a = Math.Atan(y / x);
if (x < 0)
{
a += Math.PI;
}
float size = (float)Math.Sqrt(x * x + y * y);
double newAngel = a + angle;
float newX = ((float)Math.Cos(newAngel) * size);
float newY = ((float)Math.Sin(newAngel) * size);
return pivot + new SizeF(newX, newY);
}
The above code computes the frame of the ellipse (proir to the rotation) at points p1, p2, ..., p6. And then, draws the ellipse as 2 beziers with the ellipse frame rotated points.
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();
}
I'm trying to set the transparency of a circle on a bitmap in C# .NET Winforms. I tried doing it using Graphics.DrawEllipse like this:
private void setCircleAlpha(int alpha, ref Bitmap b, ColumnVector2 pos, int diameter)
{
Graphics g = Graphics.FromImage(b);
SolidBrush sb = new SolidBrush(Color.FromArgb(0, 0, 0, 0));
g.FillEllipse(sb, pos.X, pos.Y, diameter, diameter);
}
but that does not do what I want as it draws a transparent circle over the image instead of setting the transparency of that circle.
I have resorted to using this extremely slow code:
private void setCircleAlpha(int alpha, ref Bitmap b, ColumnVector2 pos, int diameter)
{
//Calculate the square root of the radius
float radius = diameter / 2;
float sqRadius = radius * radius;
//Calculate the centre of the circle
ColumnVector2 centre = pos + new ColumnVector2(radius, radius);
for (int x = (int)pos.X; x < pos.X + diameter; x++)
{
for (int y = (int)pos.Y; y < pos.Y + diameter; y++)
{
//Calculate the distance between the centre of the circle and the point being tested
ColumnVector2 vec = new ColumnVector2(x, y) - centre;
//If the distance between a point and the centre of a circle is less than the radius of that circle then that point is in the circle.
//Calculate distance using pythagoras (a^2 + b^2 = c^2)
//Square both the distance and radius to eliminate need for square root
float sqDist = (vec.X * vec.X) + (vec.Y * vec.Y);
if (sqDist < sqRadius)
{
b.SetPixel(x, y, Color.FromArgb(alpha, b.GetPixel(x, y)));
}
}
}
}
My question is: Is there a better/faster way to do this?
Please note that I'm not asking for faster circle generation algorithms, rather I am asking for alternative graphics options.
Using Hans Passant's comment I got this working:
private void setCircleAlpha(int alpha, ref Bitmap b, ColumnVector2 pos, int diameter)
{
Graphics g = Graphics.FromImage(b);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
SolidBrush sb = new SolidBrush(Color.FromArgb(alpha, 0, 0, 0));
g.FillEllipse(sb, pos.X, pos.Y, diameter, diameter);
}
I am trying to use the combination of the timer class and a codeproject to rotate an image smoothly in a picture box control (Visual Studio 2010 C#). The problem I am having is either the picture doesn't rotate or I get a threading exception. Something about "the object is in use elsewhere". Here's the main parts of the code, I would greatly appreciate any help you can provide. Thank you.
private void Form1_Load(object sender, EventArgs e)
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
}
public static Bitmap RotateImage(Image image, float angle)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
bool bNoClip = false;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float angle, bool bNoClip)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle, bool bNoClip)
{
int W, H, X, Y;
if (bNoClip)
{
double dW = (double)image.Width;
double dH = (double)image.Height;
double degrees = Math.Abs(angle);
if (degrees <= 90)
{
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dH * dSin + dW * dCos);
H = (int)(dW * dSin + dH * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
else
{
degrees -= 90;
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dW * dSin + dH * dCos);
H = (int)(dH * dSin + dW * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
}
else
{
W = image.Width;
H = image.Height;
X = 0;
Y = 0;
}
//create a new empty bitmap to hold rotated image
Bitmap bmpRet = new Bitmap(W, H);
bmpRet.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(bmpRet);
//Put the rotation point in the "center" of the image
g.TranslateTransform(rotateAtX + X, rotateAtY + Y);
//rotate the image
g.RotateTransform(angle);
//move the image back
g.TranslateTransform(-rotateAtX - X, -rotateAtY - Y);
//draw passed in image onto graphics object
g.DrawImage(image, new PointF(0 + X, 0 + Y));
return bmpRet;
}
I think you should invalidate and paint in you OnPaint or OnDraw event
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
// this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
pictureBox1.Invalidate();
}
In your form you should enable this styles for a better perfomance and smoothness
SetStyle( ControlStyles.ResizeRedraw, true );
SetStyle( ControlStyles.UserPaint, true );
SetStyle( ControlStyles.AllPaintingInWmPaint, true );
SetStyle( ControlStyles.OptimizedDoubleBuffer, true );