Cyberdrew
Cyberdrew

Reputation: 1842

GDI+ Rotate Gauge Needle

UPDATE Final code I have to make it work based on the answers below:

public static byte[] Test(List<TestRange> ranges, int value)
        {
            var min = ranges.Min(t => t.MinValue);
            var max = ranges.Max(t => t.MaxValue);
            var rangeTotal = max - min;
            var valueAngle = ((((float)value-min) / (float)rangeTotal) * (float)270) + (float)135;
            using (var bmp = new Bitmap(500, 500))
            {
                using (var g = Graphics.FromImage(bmp))
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
                    var rec = new Rectangle(0, 0, 500, 500);
                    g.FillPie(new SolidBrush(Color.White), rec, 45, 90);
                    var startDeg = 135f;
                    foreach (var item in ranges.OrderBy(o => o.MinValue))
                    {
                        g.FillPie(new SolidBrush(ColorTranslator.FromHtml(item.Color)), rec, startDeg,
                            ((item.MaxValue - (float) item.MinValue)/rangeTotal)*270);
                        startDeg = startDeg + (((item.MaxValue - (float) item.MinValue)/rangeTotal)*270);
                    }
                    g.FillEllipse(new SolidBrush(Color.White), new Rectangle(100, 100, 300, 300));

                    using (var needle = Graphics.FromImage(bmp))
                    {
                        needle.TranslateTransform(250, 250);
                        needle.RotateTransform(valueAngle);
                        needle.TranslateTransform(-68, -39);
                        needle.DrawImage(Image.FromFile(@"C:\temp\needle.png"), new PointF(0,0));
                    }

                    g.FillRectangle(new SolidBrush(Color.Black), new RectangleF(new PointF(150,375), new SizeF(200,72)));
                    var sf = new StringFormat
                    {
                        Alignment = StringAlignment.Center,
                        LineAlignment = StringAlignment.Center
                    };
                    g.DrawString(value.ToString(), new Font(FontFamily.GenericSansSerif, 40), Brushes.White, new PointF(250, 415), sf);

                }
                using (var ms = new MemoryStream())
                {
                    bmp.Save(ms, ImageFormat.Png);
                    return ms.ToArray();
                }

            }
        }

I am trying to make a custom GDI+ gauge chart. I have everything working except the needle rotation. I have read as many articles as I can find but cannot figure out how to do it successfully. I can calculate the angles on my own, but I need to rotate the needle from a point within the needle image. 68px, 39px (X,Y from left top). Any help would be appreciated. Here is the code:

public static void Test(List<TestRange> ranges, int value)
        {
            var min = ranges.Min(t => t.MinValue);
            var max = ranges.Max(t => t.MaxValue);
            var rangeTotal = max - min;
            using (var bmp = new Bitmap(500, 500))
            {
                using (var g = Graphics.FromImage(bmp))
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
                    var rec = new Rectangle(0, 0, 500, 500);
                    g.FillPie(new SolidBrush(Color.White), rec, 45, 90);
                    var startDeg = 135f;
                    foreach (var item in ranges.OrderBy(o => o.MinValue))
                    {
                        g.FillPie(new SolidBrush(ColorTranslator.FromHtml(item.Color)), rec, startDeg,
                            ((item.MaxValue - (float) item.MinValue)/rangeTotal)*270);
                        startDeg = startDeg + (((item.MaxValue - (float) item.MinValue)/rangeTotal)*270);
                    }
                    g.FillEllipse(new SolidBrush(Color.White), new Rectangle(100, 100, 300, 300));

                    //Needle logic
                    using (var needle = Graphics.FromImage(bmp))
                    {
                        //var m = new Matrix();
                        //m.RotateAt(180, new PointF(68,39));
                        //needle.Transform = m;
                        //needle.TranslateTransform(250, 250);
                        //needle.RotateTransform(180);
                        needle.DrawImage(Image.FromFile(@"C:\temp\needle.png"), new PointF(182, 211));
                    }

                    g.FillRectangle(new SolidBrush(Color.Black), new RectangleF(new PointF(150,375), new SizeF(200,72)));
                    var sf = new StringFormat
                    {
                        Alignment = StringAlignment.Center,
                        LineAlignment = StringAlignment.Center
                    };
                    g.DrawString(value.ToString(), new Font(FontFamily.GenericSansSerif, 40), Brushes.White, new PointF(250, 415), sf);

                }
                bmp.Save("C:\\temp\\Test.png", ImageFormat.Png);
            }
        }

Here is the image it outputs: enter image description here

Here is the needle.png file: enter image description here

Upvotes: 1

Views: 1077

Answers (2)

RogerN
RogerN

Reputation: 3821

The trick is to translate the coordinate system so that your desired pivot point is at the origin, and then draw the image at 0,0:

using (var needle = Graphics.FromImage(bmp))
{
    needle.TranslateTransform(182, 211);
    needle.RotateTransform(angle);
    needle.TranslateTransform(-68, -39);
    needle.DrawImage(Image.FromFile(@"C:\temp\needle.png"), new PointF(0, 0));
}

Upvotes: 1

Jens
Jens

Reputation: 6375

You need to combine the rotation with a translation transformation and actually draw the image around the origin point.

public void DrawImageRotated(Graphics g, Image Image, Point Location, Single Angle)
{
    g.ResetTransform();
    g.RotateTransform(Angle);
    g.TranslateTransform(Location.X, Location.Y, Drawing2D.MatrixOrder.Append);
    g.DrawImageUnscaled(Image, -Image.Width / 2, -Image.Height / 2);
    g.ResetTransform();
}

enter image description here

Upvotes: 1

Related Questions