Problem beim Zeichnen eines Polygons



  • Hallo zusammen,

    ich seh gerade den Wald vor lauter Bäumen nicht mehr. Ich versuche, einen Pfeil mit GDI+ (C#) zu zeichnen. Der Kreis am hinteren Ende ist später für die Beschriftung in Form einer Nummer gedacht. Die Pfeile sollen Schritte in einem vorgegebenen Ablauf darstellen (Kalibrierung von Hardware). Dafür habe ich eine Klasse geschrieben, die einen solchen Pfeil repräsentiert und von einem Punkt aus die entsprechenden Punkte für ein Polygon berechnet. Ich habe mir unterdessen schon mind. 10 Zeichnungen auf Papier gemacht, und meine Berechnungen überprüft. Trotzdem sehe ich keine Lösung für das folgende Problem:

    Ich nutze die Methode "FillPolygon" der Graphics Klasse, um die berechneten Punkte in die Fläche einer Control zu zeichnen. Hier der aktuelle Stand:

    Hit me

    ... und hier ohne Beschriftung der Punkte:

    Hit me, too

    (Am besten das Bild abspeichern und in einem Programm zoomen, bis man die einzelnen Pixel sieht.) -> Die roten Kreuze wurden mit der Funktion DrawLine an den berechneten Eckpunkten des Polygons eingezeichnet, um das Problem zu visualisieren.

    Kopfzerbrechen bereiten mir die Eckpunkte. Wenn man sich ein Rechteck zeichnen lässt mit FillPolygon, dann entsprechenden die Eckpunkte des Rechtecks den mitgegebenen Punkten. Bei meinem Pfeil ist das nicht der Fall, und ich kann mir nicht erklären, WTF ich falsch mache...

    Klasse für einen Pfeil (mit Methode für Berechnung von Polygon-Punkten:

    public class StepArrow
    {
            int width;
            int height;
            int totArrLen;
            int peakLen;
            int peakHeight;
            int arrLen;
            int arrHeight;
            int circleRad;
            int halfHeight;
            int halfArrHeight;
            int halfPeakHeight;
            int peakArrDiff;
    
            public StepArrow()
            {
                width = 140;
                height = 81;
                totArrLen = 120;
                peakHeight = height;
                peakLen = 30;
                arrHeight = 61;
                arrLen = totArrLen - peakLen;
                circleRad = width - totArrLen;
    
                halfHeight = height / 2;
                halfArrHeight = arrHeight / 2;
                halfPeakHeight = peakHeight / 2;
                peakArrDiff = (peakHeight - arrHeight) / 2;
            }
    
            public void GetPolygonPoints(Point startPoint, out Point[] points, out Point circleCenter)
            {
                points = new Point[7];
    
                // Center of the circle.
                circleCenter = new Point(startPoint.X + circleRad, startPoint.Y + halfHeight);
                // Left upper corner of the arrow.
                points[0] = new Point(circleCenter.X, circleCenter.Y - halfArrHeight);
                // Right upper corner of the arrow.
                points[1] = new Point(points[0].X + arrLen, points[0].Y);
                // Upper arrow peak.
                points[2] = new Point(points[1].X, points[1].Y - peakArrDiff);
                // Right arrow peak.
                points[3] = new Point(points[2].X + peakLen, points[2].Y + halfPeakHeight);
                // Lower arrow peak.
                points[4] = new Point(points[2].X, points[2].Y + peakHeight);
                // Right lower corner of the arrow.
                points[5] = new Point(points[1].X, points[1].Y + arrHeight);
                // Left lower corner of the arrow.
                points[6] = new Point(points[0].X, points[0].Y + arrHeight);
            }
    }
    

    Und hier die Control, die später mehrere solcher Pfeile verwalten/platzieren soll (NOCH NICHT FERTIG!):

    public class ProgressIndicator : Control
    {
            private List<StepArrow> steps;
            private bool showGrid;
    
            public ProgressIndicator()
            {
                steps = new List<StepArrow>();
    
                // Test
                steps.Add(new StepArrow());
                Width = steps[0].Width;
                Height = steps[0].Height;
                showGrid = false;
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                Point[] stepArrPoints;
                Point circleCenter;
                steps[0].GetPolygonPoints(new Point(0, 0), out stepArrPoints, out circleCenter);
                g.FillPolygon(Brushes.Silver, stepArrPoints);
                g.FillEllipse(Brushes.LightSkyBlue, circleCenter.X - steps[0].CircleRadius,
                    circleCenter.Y - steps[0].CircleRadius, 2 * steps[0].CircleRadius,
                    2 * steps[0].CircleRadius);
    
                if (showGrid)
                {
                    Pen crossPen = new Pen(Color.FromArgb(125, 255, 0, 0), 1);
                    drawCross(g, circleCenter, crossPen);
                    g.DrawString("(" + circleCenter.X + "/" + circleCenter.Y + ")", new Font(new FontFamily("Arial"),
                            7, FontStyle.Regular),
                            Brushes.Black, circleCenter.X - 20, circleCenter.Y);
                    for (int i = 0; i < stepArrPoints.Length; i++)
                    {
                        Point p = stepArrPoints[i];
                        drawCross(g, p, crossPen);
                        g.DrawString(i + "(" + p.X + "/" + p.Y + ")", new Font(new FontFamily("Arial"),
                            7, FontStyle.Regular), 
                            Brushes.Black, p.X - 20, p.Y);
                    }
                }
            }
    
            private void drawCross(Graphics g, PointF point, Pen pen)
            {
                g.DrawLine(pen, new PointF(point.X - 3, point.Y), new PointF(point.X + 3, point.Y));
                g.DrawLine(pen, new PointF(point.X, point.Y - 3), new PointF(point.X, point.Y - 1));
                g.DrawLine(pen, new PointF(point.X, point.Y + 1), new PointF(point.X, point.Y + 3));
            }
    
            protected override void OnMouseClick(MouseEventArgs e)
            {
                showGrid = !showGrid;
                Invalidate();
            }
    }
    

    Ich habe bereits versucht, das ganze in einen GraphicsPath mit AddPolygon abzufüllen, und anschliessend per FillPath in die Control zu zeichnen, ohne Erfolg. Auch mit den Eigenschaften SmoothingMode und InterpolationMode der Graphics-Klasse habe ich ein wenig herumexperimentiert, ebenfalls erfolglos...

    Ich hoffe mir kann jemand helfen, der Tag war bisher echt unproduktiv. Verdammte Montage 😉



  • PapaNoah schrieb:

    Kopfzerbrechen bereiten mir die Eckpunkte. Wenn man sich ein Rechteck zeichnen lässt mit FillPolygon, dann entsprechenden die Eckpunkte des Rechtecks den mitgegebenen Punkten. Bei meinem Pfeil ist das nicht der Fall, und ich kann mir nicht erklären, WTF ich falsch mache...

    Also die "mitgegebenen Punkte" sind die Kreuze, ja?

    Bitte beschreibe das Problem nochmal, ich weiß nicht genau wo es liegt.
    Und falls Du ein kompilierbares Minimalbeispiel hochlädst, wäre das praktisch.



  • Ja genau, die Kreuze entsprechen den mitgegebenen Punkten.

    Das Problem ist schwer in Worte zu fassen, aber ich versuchs trotzdem:

    Lad dir mal eines der Bilder herunter, die ich dem Post angehängt habe. Öffne es in einem Bildbearbeitungsprogramm und zoome, bis zu die einzelnen Pixel erkennen kannst. Was mich irritiert, ist die Tatsache, dass die Kreuze nicht auf der Fläche des Polygons liegen. Der obere Spitz des Pfeils ist zum.Beispiel zu kurz, im Vergleich zum unteren. Oben liegen die Kreuze teilweise auf der Fläche, unten jedoch gar nicht. Der Abstand von den Ecken zu den Spitzen sollte oben und unten gleich sein... ich habe den Source auf dem Rechner auf der Arbeit, kann dir aber noch ein Beispiel zusammensetzen.



  • So, hier ein kompilierbares Beispiel:

    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace PolygonTest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                ProgressIndicator indic = new ProgressIndicator();
                Width = 300;
                Height = 300;
                Controls.Add(indic);
            }
        }
    
        public class StepArrow
        {
            int width;
            int height;
            int totArrLen;
            int peakLen;
            int peakHeight;
            int arrLen;
            int arrHeight;
            int circleRad;
            int halfHeight;
            int halfArrHeight;
            int halfPeakHeight;
            int peakArrDiff;
    
            public int Width
            {
                get { return width; }
                set { width = value; }
            }
    
            public int Height
            {
                get { return height; }
                set { height = value; }
            }
    
            public int CircleRadius
            {
                get { return circleRad; }
                set { circleRad = value; }
            }
    
            public StepArrow()
            {
                width = 140;
                height = 81;
                totArrLen = 120;
                peakHeight = height;
                peakLen = 30;
                arrHeight = 61;
                arrLen = totArrLen - peakLen;
                circleRad = width - totArrLen;
    
                halfHeight = height / 2;
                halfArrHeight = arrHeight / 2;
                halfPeakHeight = peakHeight / 2;
                peakArrDiff = (peakHeight - arrHeight) / 2;
            }
    
            public void GetPolygonPoints(Point startPoint, out Point[] points, out Point circleCenter)
            {
                points = new Point[7];
    
                // Center of the circle.
                circleCenter = new Point(startPoint.X + circleRad, startPoint.Y + halfHeight);
                // Left upper corner of the arrow.
                points[0] = new Point(circleCenter.X, circleCenter.Y - halfArrHeight);
                // Right upper corner of the arrow.
                points[1] = new Point(points[0].X + arrLen, points[0].Y);
                // Upper arrow peak.
                points[2] = new Point(points[1].X, points[1].Y - peakArrDiff);
                // Right arrow peak.
                points[3] = new Point(points[2].X + peakLen, points[2].Y + halfPeakHeight);
                // Lower arrow peak.
                points[4] = new Point(points[2].X, points[2].Y + peakHeight);
                // Right lower corner of the arrow.
                points[5] = new Point(points[1].X, points[1].Y + arrHeight);
                // Left lower corner of the arrow.
                points[6] = new Point(points[0].X, points[0].Y + arrHeight);
            }
        }
    
        public class ProgressIndicator : Control
        {
            private List<StepArrow> steps;
            private bool showGrid;
    
            public ProgressIndicator()
            {
                steps = new List<StepArrow>();
    
                // Test
                steps.Add(new StepArrow());
                Width = steps[0].Width;
                Height = steps[0].Height;
                showGrid = false;
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                Point[] stepArrPoints;
                Point circleCenter;
                steps[0].GetPolygonPoints(new Point(0, 0), out stepArrPoints, out circleCenter);
                g.FillPolygon(Brushes.Silver, stepArrPoints);
                g.FillEllipse(Brushes.LightSkyBlue, circleCenter.X - steps[0].CircleRadius,
                    circleCenter.Y - steps[0].CircleRadius, 2 * steps[0].CircleRadius,
                    2 * steps[0].CircleRadius);
    
                if (showGrid)
                {
                    Pen crossPen = new Pen(Color.FromArgb(125, 255, 0, 0), 1);
                    drawCross(g, circleCenter, crossPen);
                    g.DrawString("(" + circleCenter.X + "/" + circleCenter.Y + ")", new Font(new FontFamily("Arial"),
                            7, FontStyle.Regular),
                            Brushes.Black, circleCenter.X - 20, circleCenter.Y);
                    for (int i = 0; i < stepArrPoints.Length; i++)
                    {
                        Point p = stepArrPoints[i];
                        drawCross(g, p, crossPen);
                        g.DrawString(i + "(" + p.X + "/" + p.Y + ")", new Font(new FontFamily("Arial"),
                            7, FontStyle.Regular),
                            Brushes.Black, p.X - 20, p.Y);
                    }
                }
            }
    
            private void drawCross(Graphics g, PointF point, Pen pen)
            {
                g.DrawLine(pen, new PointF(point.X - 3, point.Y), new PointF(point.X + 3, point.Y));
                g.DrawLine(pen, new PointF(point.X, point.Y - 3), new PointF(point.X, point.Y - 1));
                g.DrawLine(pen, new PointF(point.X, point.Y + 1), new PointF(point.X, point.Y + 3));
            }
    
            protected override void OnMouseClick(MouseEventArgs e)
            {
                showGrid = !showGrid;
                Invalidate();
            }
        } 
    }
    


  • DrawPolygon trifft Deine Punkte genau. Wenn Du also den Rand nochmal extra zeichnest, ist das Problem behoben.

    Zeile 114. g.FillPolygon(Brushes.Silver, stepArrPoints);
               g.DrawPolygon(Pens.Silver, stepArrPoints);
    

    Ob das ein Bug von FillPolygon oder per Design so gewollt ist, kann ich Dir nicht sagen.



  • Vielen Dank für die Antwort, damit kann ich auf jeden Fall etwas anfangen.

    Let's call it a bug-feature 😉


Anmelden zum Antworten