How to flip alignment of drawn content in a panel - c#

I am using two panels for drawing ruler along the sides (top and left) of a picture box. It works, but now my requirement is to flip the direction of the ruler so that the line starts from the picture box and the text (numbers) are the top. How can I do this?
My code is as follows:
private void panel2_Paint(object sender, PaintEventArgs e)//left Panel
{
Graphics g = e.Graphics;
g.PageUnit = GraphicsUnit.Millimeter;
int step = 1;
int length = panelleft.Height / 3;
int small = 5;
int big = 10;
int number = 10;
int scale = 10;
float stroke = 2.5f;
for (int i = 0; i < length; i += step)
{
float d = 1;
if (i % small == 0)
{
if (i % big == 0)
{
d = 3;
}
else
{
d = 2;
}
}
g.DrawLine(this.pen, 0f, i,d * stroke, i);
if ((i % number) == 0)
{
string text = (i / scale).ToString();
SizeF size = g.MeasureString(text, this.Font, length, this.format);
g.DrawString(text, this.Font, Brushes.Black,d * stroke, i - size.Width-1 / 2 , this.format);
}
}
}
private void panel3_Paint(object sender, PaintEventArgs e)// top panel
{
Graphics g = e.Graphics;
g.PageUnit = GraphicsUnit.Millimeter;
int step = 1;//incremnent
int length = paneltop.Width / 3; //panelinte widthinte pakuthi mathi bcs namml oru point gap vittanu line varakunnath
int small = 5;//cheriya vark ulla length
int big = 10;//valiya vark ulla length
int number = 10;//units 1cm=10 units
float stroke = 2.5f;
for (int i = 0; i < length; i += step)
{
float d = 1;
if (i % small == 0)//cheriya line
{
if (i % big == 0)//valiya line
{
d = 3; //varyude length
}
else
{
d = 2;//varyude length
}
}
g.DrawLine(this.pen, i, 0f, i, d * stroke);//lines varakunnu
if ((i % number) == 0)//0,1,,2
{
string text = (i / number).ToString();//1,2,3 ennu ezhuthan
SizeF size = g.MeasureString(text, this.Font, length, this.format);//ezhuthuna stringnte length ariyan// one digit length 1.618635,2 digit length3.23727
g.DrawString(text, this.Font, Brushes.Black, i - size.Width / 2, d * stroke, this.format);//Y constant ayirikum (d* stroke) ennu koduthath line kazhinju string varan anu alenkil overlapp cheyum
// ( X ) ( Y )
}
}
}
I also want to show a horizontal and vertical line, and they must be pointed to the ruler when the user moves the mouse over the image.
Required output sample:

Since you have set the Graphics to mm you need to calculate the conversion factor for the controls' pixel sizes:
float dpi = e.Graphics.DpiX;
float factor = 25.4f / dpi;
with this you can simply adapt your DrawString and DrawLine calls like this:
In panel2_Paint:
float w = panelleft.Width * factor;
g.DrawLine(this.pen, w - d * stroke, i, w, i);
g.DrawString(text, this.Font, Brushes.Black,
w - d * stroke - size.Width, i - size.Width - 1 / 2, this.format);
and in panel3_Paint:
float h = paneltop.Height * factor;
g.DrawLine(this.pen, i, h - d * stroke, i, h);
g.DrawString(text, this.Font, Brushes.Black,
i - size.Width / 2, h - d * stroke - size.Height, this.format);
To show the Cursor lines you can use this:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Point mp = pictureBox1.PointToClient(Cursor.Position);
if (e.ClipRectangle.Contains(mp))
{
e.Graphics.DrawLine(Pens.Red, 0, mp.Y, e.ClipRectangle.Width, mp.Y);
e.Graphics.DrawLine(Pens.Red, mp.X, 0, mp.X, e.ClipRectangle.Height);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pictureBox1.Invalidate();
}
Here is the result, using a few assumptions:
panelleft = panel2;
paneltop = panel3;
format = StringFormat.GenericTypographic;
pen = new Pen(Color.Black, 0.25f);
In theory you should use dpiX and dpiY respectively to get two factors.

For the top one, you want to change your DrawLine and your DrawString calls to be inverse of what they currently are.
In the Panel3 paint:
g.DrawLine(this.pen, i, 0f, i, d * stroke);//lines varakunnu
This draws a line from (i, 0) to (i, d*stroke). We want to invert this line, so we will do:
g.DrawLine(this.pen, i, panel3.Height, i, (panel3.Height - d * stroke));//lines varakunnu
We also want to adjust the label as well, so we will change this:
g.DrawString(text, this.Font, Brushes.Black, i - size.Width / 2, d * stroke, this.format);
to
g.DrawString(text, this.Font, Brushes.Black, i - size.Width / 2, (panel3.Height - d * stroke - size.Height), this.format);
or
g.DrawString(text, this.Font, Brushes.Black, i - size.Width / 2, (panel3.Height - d * stroke), this.format);
I'm not sure if you need to compensate for size.Height or not, since I can't test your code.

Related

How can i fill part of image with color?

I have image which i add in my form.How can i fill part of image?
I have this
What I'm trying to achieve:
To floodfill an area you need a foodfill routine and very little else.
See this example:
It uses two pictureboxes, also a label to display the chosen color.
And two mouse click events, one to pick the color:
private void pictureBoxPalette_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxPalette, e.Location);
lbl_color.BackColor = ((Bitmap)pictureBoxPalette.Image).GetPixel(sPt.X, sPt.Y);
}
..and one to call the fill:
private void pictureBoxTgt_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxTgt, e.Location);
Bitmap bmp = (Bitmap)pictureBoxTgt.Image;
Color c0 = bmp.GetPixel(sPt.X, sPt.Y);
Fill4(bmp, sPt, c0, lbl_color.BackColor);
pictureBoxTgt.Image = bmp;
}
The Floodfill routine is taken from this post; it is basically a direct implementation of a wikipedia algorithm..:
static void Fill4(Bitmap bmp, Point pt, Color c0, Color c1)
{
Color cx = bmp.GetPixel(pt.X, pt.Y);
if (cx.GetBrightness() < 0.01f) return; // optional, to prevent filling a black grid
Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size);
Stack<Point> stack = new Stack<Point>();
int x0 = pt.X;
int y0 = pt.Y;
stack.Push(new Point(x0, y0) );
while (stack.Any() )
{
Point p = stack.Pop();
if (!bmpRect.Contains(p)) continue;
cx = bmp.GetPixel(p.X, p.Y);
if (cx.ToArgb() == c0.ToArgb()) //*
{
bmp.SetPixel(p.X, p.Y, c1);
stack.Push(new Point(p.X, p.Y + 1));
stack.Push(new Point(p.X, p.Y - 1));
stack.Push(new Point(p.X + 1, p.Y));
stack.Push(new Point(p.X - 1, p.Y));
}
}
}
Note: (*) Color equality will fail if one of the colors is a known or named color. So we need to convert to a common format..
Update
I have updated the code to include a function that will scale a mouse click location to an image pixel point; now it will work with SizeMode=StretchImage as well, so you can work on the whole image..
static Point scaledPoint(PictureBox pb, Point pt)
{
float scaleX = 1f * pb.Image.Width / pb.ClientSize.Width;
float scaleY = 1f * pb.Image.Height / pb.ClientSize.Height;
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}
Of course you can then save the Image.
Note that your original image is 4bpp and must be converted to 24bpp or better before coloring..
Also note that for SizeMode=Zoom the calculations are a little more involved. Here is an example that should work with any SizeMode.:
static Point scaledPoint(PictureBox pbox, Point pt)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
int left = 0;
int top = 0;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize) return pt;
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
{
left = (sp.Width - si.Width) / 2;
top = (sp.Height - si.Height) / 2;
return new Point(pt.X - left, pt.Y - top);
}
if (pbox.SizeMode == PictureBoxSizeMode.Zoom)
{
if (1f * si.Width / si.Height < 1f * sp.Width / sp.Height)
left = (sp.Width - si.Width * sp.Height / si.Height) / 2;
else
top = (sp.Height - si.Height * sp.Width / si.Width) / 2;
}
pt = new Point(pt.X - left, pt.Y - top);
float scaleX = 1f * pbox.Image.Width / (pbox.ClientSize.Width - 2 * left) ;
float scaleY = 1f * pbox.Image.Height / (pbox.ClientSize.Height - 2 * top);
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}

Detect passing of rectangle over yellow pixel

I have a query regarding the best approach to detect when a moving and potentially rotated rectangle passes over a yellow pixel of a Panel's background image.
I have a method which accepts an Image and a Point, and returns true if that point is that of a yellow pixel. I require this colour detection for the function of my game, which resets the car (player) if it drives over the yellow borders of the track. This method is shown below:
private Boolean isYellow(Image image, Point point)
{
Bitmap bitmap = new Bitmap(image);
Color color = bitmap.GetPixel(point.X, point.Y);
return (color.R > 220 && color.G > 220 && color.B < 200);
}
Previously, to detect if the player rectangle passes over yellow, I checked against the location of the rectangle, as provided by the X and Y values of the object. The issue with this is that the location is the top left corner of a horizontal rectangle, meaning the car can drive almost entirely off the track without detection occurring.
I'd like to fix this by checking all points covered by the rectangle. This is not as simple as it may seem as the rectangle is likely to be rotated. My drawing and movement logic is shown below:
public void draw(Graphics g)
{
int dx = rectangle.X + (rectangle.Height / 2);
int dy = rectangle.Y + (rectangle.Width / 2);
g.ScaleTransform(xScale, yScale);
g.TranslateTransform(dx, dy);
g.RotateTransform((float) ((180 * angle) / Math.PI));
g.TranslateTransform(-dx, -dy);
g.DrawImage(image, rectangle.X, rectangle.Y);
g.ResetTransform();
}
public void move(uRaceGame game, Panel panel)
{
double cos = Math.Cos(angle), sin = Math.Sin(angle);
int xLocation = 200;
int yLocation = 200;
xLocation = (int) Math.Floor(rectangle.X + (cos * game.moveDir * 60));
yLocation = (int) Math.Floor(rectangle.Y + (sin * game.moveDir * 60));
angle = (angle + (game.rotateDir * (Math.PI / 128))) % (Math.PI * 2);
if (xLocation * xScale > panel.Width - (rectangle.Width * cos) || yLocation * yScale > panel.Height - (rectangle.Width * sin) - 5 || xLocation * xScale < 0 || yLocation * yScale < 5) return;
rectangle.Location = new Point(xLocation, yLocation);
}
I tried but failed to create a method which translates the coords of the corner and figures out the middle of the rectangle, but this does not work, and the yellow detection fires in very obscure places:
public Point getCentre()
{
int cX = (int) (rectangle.X + ((rectangle.Width / 2) / xScale)), cY = (int) (rectangle.Y + ((rectangle.Height / 2) / yScale));
float tempX = (rectangle.X - cX), tempY = (rectangle.Y - cY);
double rX = (tempX * Math.Cos(angle)) - (tempY * Math.Sin(angle));
double rY = (tempX * Math.Sin(angle)) - (tempY * Math.Cos(angle));
return new Point((int) ((rX + cX) * xScale), (int) ((rY + cY) * yScale));
}
I'd really appreciate any suggestions on how to tackle this. I included the translation and yellow detection code in case I'm miles off in my attempt and someone else has a better idea.
Thank you very much.
There are two approaches that come to my mind:
You can create loops that go along the tilted sides of the car rectangle
Or you can copy the car to an untilted bitmap and loop over it normally.
Here is an example of the second approach.
It uses a LockBits method that detects Yellow with your code in a Bitmap.
And it prepares that bitmap by copying it from the original BackgroundImage un-rotated.
Here is the result, including a control Panel that shows the untilted Rectangle:
Here is the yellow finder function. It uses Lockbits for speed:
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
public bool testForYellowBitmap(Bitmap bmp)
{
Size s1 = bmp.Size;
PixelFormat fmt = new PixelFormat();
fmt = bmp.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
byte bpp1 = 4;
if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3;
else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4; else return false; // throw!!
int size1 = bmp1Data.Stride * bmp1Data.Height;
byte[] data1 = new byte[size1];
System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
for (int y = 0; y < s1.Height; y++)
{
for (int x = 0; x < s1.Width; x++)
{
Color c1;
int index1 = y * bmp1Data.Stride + x * bpp1;
if (bpp1 == 4)
c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2],
data1[index1 + 1], data1[index1 + 0]);
else c1 = Color.FromArgb(255, data1[index1 + 2],
data1[index1 + 1], data1[index1 + 0]);
if (c1.R > 220 && c1.G > 220 && c1.B < 200)
{ bmp.UnlockBits(bmp1Data); return true; }
}
}
bmp.UnlockBits(bmp1Data);
return false;
}
I prepare the Bitmap to compare in the MouseMove. The variables w, h, w2, h2 hold the width, height and halves of that of the car's size. The source bitmap is in drawPanel1.BackgroundImage. The current angle is in a TrackBar tr_a.Value. For further control I also display the rotated car rectangle in White.
private void drawPanel1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left))
{
Size sz = drawPanel1.BackgroundImage.Size;
Rectangle rectSrc = new Rectangle(e.X - w2, e.Y - h2, w, h);
Rectangle rectTgt = new Rectangle(e.X - w, e.Y - h, 2 * w, 2 * h);
using (Graphics g = drawPanel1.CreateGraphics()) // start optional
{
g.TranslateTransform(e.X, e.Y);
g.RotateTransform(trb_a.Value);
g.TranslateTransform(-e.X, -e.Y);
drawPanel1.Refresh();
g.DrawRectangle(Pens.White, rectSrc);
}
using (Graphics g = drawPanel2.CreateGraphics())
{ // end optional
using (Bitmap bmp = new Bitmap(sz.Width, sz.Height))
using (Graphics g2 = Graphics.FromImage(bmp))
{
g2.TranslateTransform(e.X, e.Y);
g2.RotateTransform(-trb_a.Value);
g2.TranslateTransform(-e.X, -e.Y);
g2.DrawImage(drawPanel1.BackgroundImage, rectTgt, rectTgt,
GraphicsUnit.Pixel);
drawPanel2.Refresh();
g.DrawImage(bmp, rectSrc, rectSrc, GraphicsUnit.Pixel);
Text = testForYellowBitmap(bmp) ? "!!YELLOW!!" : "";
}
}
}
The first approach would use a similar LockBits method, but with loops inside that go along the rotated sides of the car rectangle, using floats wth the loop variables to calculate the x-coordinates. Those data should be prepared on each change of car size or angle. The code is a little longer but should be a bit faster, too.
The advantage if the second approach is that by using a ClippingRegion on the Graphics object one could check an arbitrary shape while the first method can be easily modified for concave polygons but not for curved shapes.
Here is the adapted version of the checking code for the first version:
public bool testForYellowBitmapTilt(Bitmap bmp, List<int> leftPts,
List<int> rightPts, Point topLeft)
{
Size s1 = bmp.Size;
PixelFormat fmt = new PixelFormat();
fmt = bmp.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
byte bpp1 = 4;
if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3;
else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4;
else return false; // or throw!!
if (leftPts.Count != rightPts.Count) return false; // or throw!!
int size1 = bmp1Data.Stride * bmp1Data.Height;
byte[] data1 = new byte[size1];
System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
for (int y = 0; y < (leftPts.Count); y++)
{
for (int x = leftPts[y] + topLeft.X; x < rightPts[y] + topLeft.X; x++)
{
Color c1;
int index1 = (y + topLeft.Y) * bmp1Data.Stride + x * bpp1;
if (index1 > 0)
{
if (bpp1 == 4)
c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2],
data1[index1 + 1], data1[index1 + 0]);
else c1 = Color.FromArgb(255, data1[index1 + 2],
data1[index1 + 1], data1[index1 + 0]);
if (c1.R > 220 && c1.G > 220 && c1.B < 200)
{ bmp.UnlockBits(bmp1Data); return true; }
}
}
}
bmp.UnlockBits(bmp1Data);
return false;
}
The left- and rightside coordinates are stored here:
List<int> leftPts = new List<int>();
List<int> rightPts = new List<int>();
Point top = Point.Empty;
void getOuterPoints(List<PointF> corners, out List<int> leftPts,
out List<int> rightPts, out Point top)
{
leftPts = new List<int>();
rightPts = new List<int>();
PointF left = corners.Select(x => x).OrderBy(x => x.X).First();
PointF right = corners.Select(x => x).OrderByDescending(x => x.X).First();
top = Point.Round(corners.Select(x => x).OrderBy(x => x.Y).First());
PointF bottom = corners.Select(x => x).OrderByDescending(x => x.Y).First();
int w1 = -(int)(top.X - left.X);
int w2 = -(int)(left.X - bottom.X );
int h1 = (int)(left.Y - top.Y);
int h2 = (int)(bottom.Y - left.Y);
float d1 = 1f * w1 / h1;
float d2 = 1f * w2 / h2;
for (int y = 0; y < h1; y++) leftPts.Add( (int)(y * d1) );
for (int y = 0; y < h2; y++) leftPts.Add( (int)(y * d2 + w1));
for (int y = 0; y < h2; y++) rightPts.Add( (int)(y * d2));
for (int y = 0; y < h1; y++) rightPts.Add( (int)(y * d1 + w2));
}
You need to feed in the four corners as a List<PointF> in any order; the top can be anything, it will be set in the method. The coodinates are relative to the car, so they don't change when the car moves..

Defining Regions for Venn Diagram using Graphics Regions

I have created a Venn diagram using simple Graphics functions provided by WinForm in the onPaint event. Here is my code for creating the Venn.
using (Brush brushLeft = new SolidBrush(LeftVennColor))
{
leftvennPath.AddEllipse(leftVenn);
leftOnlyRegion = new Region(leftVenn);
e.Graphics.FillEllipse(brushLeft, leftVenn);
e.Graphics.DrawEllipse(pen, leftVenn);
}
using (Brush brushRight = new SolidBrush(RightVennColor))
{
rightvennPath.AddEllipse(rightVenn);
rightOnlyRegion = new Region(rightVenn);
e.Graphics.FillEllipse(brushRight, rightVenn);
e.Graphics.DrawEllipse(pen, rightVenn);
}
using (GraphicsPath circle_path = new GraphicsPath())
{
circle_path.AddEllipse(leftVenn);
commonRegion.Intersect(circle_path);
}
using (GraphicsPath circle_path = new GraphicsPath())
{
circle_path.AddEllipse(rightVenn);
commonRegion.Intersect(circle_path);
}
The Venn diagram is created, but with this code my common region is the intersection of both left and right ellipses. I want to have two separate regions out of that common area, which is separated by a line. Here is the image for that,
So basically, I need all these four regions separated and clickable ( different colors for each region ).. I use Region.IsVisible(e.location) in the mouse click event to handle the click event. Could someone please help?
Final solution:
cx0, cy0, radius0 center and radius of left circle
cx1, cy1, radius1 center and radius of right circle
The function takes the regions by ref.
private void FindRegions(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref Region rgnLeft, ref Region rgnRight)
{
//Left circle
GraphicsPath gpL = new GraphicsPath();
//Right circle
GraphicsPath gpR = new GraphicsPath();
//The right small region (yellow color)
GraphicsPath gp = new GraphicsPath();
//Points of intersection
PointF pnt1 = new PointF();
PointF pnt2 = new PointF();
Graphics g = this.CreateGraphics();
gpL.AddEllipse(new Rectangle(cx0 - radius0, cy0 - radius0, 2 * radius0, 2 * radius0));
gpR.AddEllipse(new Rectangle(cx1 - radius0, cy1 - radius1, 2 * radius1, 2 * radius1));
g.DrawPath(Pens.Red, gpL);
g.DrawPath(Pens.Blue, gpR);
int numPoints = FindCircleCircleIntersections((single)cx0, (single)cx1, (single)cy0, (single)cy1, (single)radius0, (single)radius1, ref pnt1, ref pnt2);
if (numPoints != 2)
{
//No regions
return;
}
Double theta, fe;
Double dx = (double)pnt1.X - (double)pnt2.X;
Double dy = (double)pnt1.Y - (double)pnt2.Y;
Double dist = Math.Sqrt(dx * dx + dy * dy);
PointF minPoint, maxPoint;
if (pnt2.Y < pnt1.Y)
{
minPoint = pnt2;
maxPoint = pnt1;
}
else
{
minPoint = pnt1;
maxPoint = pnt2;
}
//theta is the angle between the three points pnt1, pnt2 and left center
theta = Math.Acos((dist / 2D) / 100D);
theta = (theta * 180D) / Math.PI;
theta = 90D - theta;
theta *= 2D;
//fe is the starting angle of the point(between pnt1 and pnt2) with
//the smaller y coordinate. The angle is measured from x axis and clockwise
fe = Math.Asin( Math .Abs ( (-(Double)minPoint.Y + (double)cy0) )/ (double)radius0);
fe = (fe * 180D) / Math.PI;
if (minPoint.X > cx0 && minPoint.Y >= cy0)
{
//fe = (90 - fe) + 270;
}
else if (minPoint.X > cx0 && minPoint.Y < cy0)
{
fe = (90D - fe) + 270D;
}
else if (minPoint.X == cx0 && minPoint.Y < cy0)
{
fe = 270D;
}
else
{
fe += 180D;
}
gp.AddArc(new Rectangle(cx0 - radius0, cy0 - radius0, 2 * radius0, 2 * radius0), (float)fe, (float)theta);
gp.AddLine(maxPoint, minPoint);
gp.CloseFigure();
g.DrawPath(Pens.Green, gp);
Region rgnL = new Region(gpL);
Region rgnR = new Region(gpR);
Region rgnInt = new Region(gpL);
Region rgn = new Region(gp); //right small
rgnInt.Intersect(rgnR);
rgnInt.Exclude(rgn); //left small
g.FillRegion(Brushes.DarkGreen, rgnInt);
g.FillRegion(Brushes.DarkGray, rgn);
rgnLeft = rgnInt.Clone();
rgnRight = rgn.Clone();
g.Dispose();
rgnL.Dispose();
rgnR.Dispose();
rgnInt.Dispose();
rgn.Dispose();
gpL.Dispose();
gpR.Dispose();
gp.Dispose();
}
private int FindCircleCircleIntersections(Single cx0, Single cx1, Single cy0, Single cy1, Single radius0, Single radius1,
ref PointF intersection1, ref PointF intersection2)
{
// Find the distance between the centers.
Single dx = cx0 - cx1;
Single dy = cy0 - cy1;
Double dist = Math.Sqrt(dx * dx + dy * dy);
// See how many solutions there are.
if (dist > radius0 + radius1)
{
//No solutions, the circles are too far apart.
intersection1 = new PointF(Single.NaN, Single.NaN);
intersection2 = new PointF(Single.NaN, Single.NaN);
return 0;
}
else if (dist < Math.Abs(radius0 - radius1))
{
// No solutions, one circle contains the other.
intersection1 = new PointF(Single.NaN, Single.NaN);
intersection2 = new PointF(Single.NaN, Single.NaN);
return 0;
}
else if ((dist == 0) && (radius0 == radius1))
{
// No solutions, the circles coincide.
intersection1 = new PointF(Single.NaN, Single.NaN);
intersection2 = new PointF(Single.NaN, Single.NaN);
return 0;
}
else
{
// Find a and h.
Double a = (radius0 * radius0 - radius1 * radius1 + dist * dist) / (2 * dist);
Double h = Math.Sqrt(radius0 * radius0 - a * a);
// Find P2.
Double cx2 = cx0 + a * (cx1 - cx0) / dist;
Double cy2 = cy0 + a * (cy1 - cy0) / dist;
// Get the points P3.
intersection1 = new PointF( (Single)(cx2 + h * (cy1 - cy0) / dist), (Single)(cy2 - h * (cx1 - cx0) / dist));
intersection2 = new PointF( (Single)(cx2 - h * (cy1 - cy0) / dist), (Single)(cy2 + h * (cx1 - cx0) / dist));
// See if we have 1 or 2 solutions.
if (dist == radius0 + radius1) return 1;
return 2;
}
}
EDIT
Region has only a Fill method and no Draw one. So you cant do it with regions. GraphicPath
however HAS both Fill and Draw.
You said that you need to validate if a point is inside the region BUT you can do the same with GraphicPath
myGraphicPath.IsVisible();
So, dont use regions but paths. It is better for another reason. GraphicPath can draw AntiAlias but regions dont. Set
g.SmoothingMode = SmoothingMode.AntiAlias;
To enable AntiAlias. All goodies with paths!
Change the function name from FindRegions to FindPaths and send paths as refference:
private void FindPaths(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref GraphicsPath gpLeft, ref GraphicsPath gpRight)
The code is exactly the same, but add in the and:
private void FindPaths(int cx0, int cx1, int cy0, int cy1, int radius0, int radius1, ref GraphicsPath gpLeft, ref GraphicsPath gpRight)
{
...
...
//Above code exactly the same
//replace these
//rgnLeft = rgnInt.Clone();
//rgnRight = rgn.Clone();
//with these
GraphicsPath gpLeftSmall = (GraphicsPath)gp.Clone();
Matrix matrix = new Matrix();
PointF pntf = new PointF();
pntf.X = (float)(Math.Min((double)pnt1.X, (double)pnt2.X) + Math.Abs((double)(pnt1.X - pnt2.X) / 2D));
pntf.Y = (float)(Math.Min((double)pnt1.Y, (double)pnt2.Y) + Math.Abs((double)(pnt1.Y - pnt2.Y) / 2D));
matrix.RotateAt(180, pntf);
gpLeftSmall.Transform(matrix);
g.DrawPath(Pens.Black, gpLeftSmall); //If you want to draw it
//passed by refference
gpLeft = gpLeftSmall.Clone();
gpRight = gp.Clone();
g.Dispose();
rgnL.Dispose();
rgnR.Dispose();
rgnInt.Dispose();
rgn.Dispose();
gpL.Dispose();
gpR.Dispose();
gp.Dispose();
gpLeftSmall.Dispose();
matrix.Dispose();
}
Reference:
Determine where two circles intersect

Adding properties to a bitmap object that's created by calling another method within a constructor

I have a method that takes in a bitmap object and overlays dates and times strings over it and returns that new bitmap. The code is below.
public static Bitmap overlayBitmap(Bitmap sourceBMP, int width, int height, List<String> times, List<String> dates, IEnumerable<Color> colors) {
// Determine the new width
float newWidth = width + (width / 3.0f);
float newHeight = height + (height / 3.0f);
// Intelligent vertical + horizontal text distance calculator
float verticalDistance = height / (times.Count - 1.0f);
float horizontalDistance = width / (dates.Count - 1.0f);
Bitmap result = new Bitmap((int)newWidth, (int)newHeight);
using (Graphics g = Graphics.FromImage(result)) {
// Background color
Brush brush = new SolidBrush(colors.First());
g.FillRectangle(brush, 0, 0, newWidth, newHeight);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
// Times text configs
StringFormat stringFormatTimes = new StringFormat();
stringFormatTimes.LineAlignment = StringAlignment.Center;
stringFormatTimes.Alignment = StringAlignment.Center;
Font drawFontY = new Font("Whitney", newHeight / 70);
// Dates text configs
StringFormat stringFormatDates = new StringFormat();
stringFormatDates.LineAlignment = StringAlignment.Center;
stringFormatTimes.Alignment = StringAlignment.Center;
stringFormatDates.FormatFlags = StringFormatFlags.DirectionVertical;
Font drawFontX = new Font("Whitney", newHeight / 70);
// Location of times text
for (int i = 0; i < times.Count; i++) {
if (i % determineIncrementTimes(times.Count) == 0) {
g.DrawString(times[i], drawFontX, Brushes.White, (((newWidth - width) / 2) / 2), ((newHeight - height) / 2) + (verticalDistance * i), stringFormatTimes);
}
}
// Location of dates text
for (int i = 0; i < dates.Count; i++) {
if (i % determineIncrementDates(dates.Count) == 0) {
g.DrawString(dates[i], drawFontY, Brushes.White, ((newWidth - width) / 2) + (horizontalDistance * i), ((newHeight - height) / 2) + height, stringFormatDates);
}
}
// New X and Y Position of the sourceBMP within the new BMP.
int XPos = width / 6;
int YPos = height / 6;
// Int -> Float casting for the outline
float fXPos = width / 6.0f;
float fYPos = height / 6.0f;
float fWidth = width / 1.0f;
float fHeight = height / 1.0f;
// Draw new image at the position width/6 and height/6 with the size at width and height
g.DrawImage(sourceBMP, fXPos, fYPos, fWidth, fHeight);
g.DrawRectangle(Pens.White, fXPos, fYPos, fWidth, fHeight); // white outline
g.Dispose();
}
return result;
}
My concern is, I would like to be able, for the next developer, to easily access and set particular values that currently I've only "hardcoded" in. An example being the x-position of the time text calculated via this snippet of code:
(((newWidth - width) / 2) / 2)
Realistically I'd like to have the developer be able to access and/or set this value through simply typing in:
something.XPos = [someFloat];
How my method above is used (is pseudo-code) is as the following:
private readonly Bitmap _image;
private readonly Bitmap _overlayedImage;
public myConstructor(int someInputValues){
// some code that generates the first bitmap called _image
_newImage = overlayImage(_image, ....);
}
For reference this is the image drawn:
My question is - since some values need to be casted and initialized first, can I set my instance variables at the end of the method, before the closing brace?
public Bitmap overlayBitmap
{
get
{
// Build bitmap overlay
return overlayBitmapOutput;
}
...
}
[Edit: Answer Insufficient >> Wait]

Transcribing a polygon on a circle

i am currently try to inscribe diagonals of a decagon inside a circle
like this
in c# my approach would be creating a circle
e.Graphics.DrawEllipse(myPen, 0, 0, 100, 100);
and draw lines inside using
e.Graphics.DrawLine(myPen, 20, 5, 50, 50);
after that i would draw a decagon polygon.
currently im stuck at how to divide the circle into 10 parts/ finding the correct coordiantes of the points on the circumference of the circles because im not good in math,
i want to know how would i know the next point in a circumference of the circle the size of my circle is indicated above.
and also i want also to ask a better approach for my problem.
Thank you :)
Just for grits and shins, here's a generic implementation that will inscribe an X-sided polygon into the Rectangle you pass it. Note that in this approach I'm not actually calculating any absolute points. Instead, I am translating the origin, rotating the surface, and drawing the lines only with respect to the origin using a fixed length and an angle. This is repeated in a loop to achieve the end result below, and is very similar to commanding the Turtle in Logo:
public partial class Form1 : Form
{
PictureBox pb = new PictureBox();
NumericUpDown nud = new NumericUpDown();
public Form1()
{
InitializeComponent();
this.Text = "Inscribed Polygon Demo";
TableLayoutPanel tlp = new TableLayoutPanel();
tlp.RowCount = 2;
tlp.RowStyles.Clear();
tlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
tlp.ColumnCount = 2;
tlp.ColumnStyles.Clear();
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
tlp.Dock = DockStyle.Fill;
this.Controls.Add(tlp);
Label lbl = new Label();
lbl.Text = "Number of Sides:";
lbl.TextAlign = ContentAlignment.MiddleRight;
tlp.Controls.Add(lbl, 0, 0);
nud.Minimum = 3;
nud.Maximum = 20;
nud.AutoSize = true;
nud.ValueChanged += new EventHandler(nud_ValueChanged);
tlp.Controls.Add(nud, 1, 0);
pb.Dock = DockStyle.Fill;
pb.Paint += new PaintEventHandler(pb_Paint);
pb.SizeChanged += new EventHandler(pb_SizeChanged);
tlp.SetColumnSpan(pb, 2);
tlp.Controls.Add(pb, 0, 1);
}
void nud_ValueChanged(object sender, EventArgs e)
{
pb.Refresh();
}
void pb_SizeChanged(object sender, EventArgs e)
{
pb.Refresh();
}
void pb_Paint(object sender, PaintEventArgs e)
{
// make circle centered and 90% of PictureBox size:
int Radius = (int)((double)Math.Min(pb.ClientRectangle.Width, pb.ClientRectangle.Height) / (double)2.0 * (double).9);
Point Center = new Point((int)((double)pb.ClientRectangle.Width / (double)2.0), (int)((double)pb.ClientRectangle.Height / (double)2.0));
Rectangle rc = new Rectangle(Center, new Size(1, 1));
rc.Inflate(Radius, Radius);
InscribePolygon(e.Graphics, rc, (int)nud.Value);
}
private void InscribePolygon(Graphics G, Rectangle rc, int numSides)
{
if (numSides < 3)
throw new Exception("Number of sides must be greater than or equal to 3!");
float Radius = (float)((double)Math.Min(rc.Width, rc.Height) / 2.0);
PointF Center = new PointF((float)(rc.Location.X + rc.Width / 2.0), (float)(rc.Location.Y + rc.Height / 2.0));
RectangleF rcF = new RectangleF(Center, new SizeF(1, 1));
rcF.Inflate(Radius, Radius);
G.DrawEllipse(Pens.Black, rcF);
float Sides = (float)numSides;
float ExteriorAngle = (float)360 / Sides;
float InteriorAngle = (Sides - (float)2) / Sides * (float)180;
float SideLength = (float)2 * Radius * (float)Math.Sin(Math.PI / (double)Sides);
for (int i = 1; i <= Sides; i++)
{
G.ResetTransform();
G.TranslateTransform(Center.X, Center.Y);
G.RotateTransform((i - 1) * ExteriorAngle);
G.DrawLine(Pens.Black, new PointF(0, 0), new PointF(0, -Radius));
G.TranslateTransform(0, -Radius);
G.RotateTransform(180 - InteriorAngle / 2);
G.DrawLine(Pens.Black, new PointF(0, 0), new PointF(0, -SideLength));
}
}
}
I got the formula for the length of the side here at Regular Polygon Calculator.
One way of dealing with this is using trigonometric functions sin and cos. Pass them the desired angle, in radians, in a loop (you need a multiple of 2*π/10, i.e. a = i*π/5 for i between 0 and 9, inclusive). R*sin(a) will give you the vertical offset from the origin; R*cos(a) will give you the horizontal offset.
Note that sin and cos are in the range from -1 to 1, so you will see both positive and negative results. You will need to add an offset for the center of your circle to make the points appear at the right spots.
Once you've generated a list of points, connect point i to point i+1. When you reach the ninth point, connect it to the initial point to complete the polygon.
I don't test it, but i think it is ok.
#define DegreeToRadian(d) d * (Pi / 180)
float r = 1; // radius
float cX = 0; // centerX
float cY = 0; // centerY
int numSegment = 10;
float angleOffset = 360.0 / numSegment;
float currentAngle = 0;
for (int i = 0; i < numSegment; i++)
{
float startAngle = DegreeToRadian(currentAngle);
float endAngle = DegreeToRadian(fmod(currentAngle + angleOffset, 360));
float x1 = r * cos(startAngle) + cX;
float y1 = r * sin(startAngle) + cY;
float x2 = r * cos(endAngle) + cX;
float y2 = r * sin(endAngle) + cY;
currentAngle += angleOffset;
// [cX, cY][x1, y1][x2, y2]
}
(fmod is c++ function equals to floatNumber % floatNumber)

Categories