[C# / Drawing] Tekenen van border rond driehoek

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Face_-_LeSS
  • Registratie: September 2004
  • Niet online
Wens
Ik ben bezig met het tekenen van een soort klok. In die klok wil ik een wijzer tekenen in de vorm van een driehoek ik wil ook dat de wijzer een "border" heeft.

Probleem
De vorm van de klok lukt (schaalverdeling, getallen, ...), de achtergrond van de wijzer ook maar ik loop vast op de border van de wijzer.
Ik teken de achtergrond van de wijzer d.m.v. de methode Graphics.FillPolygon. Aan die methode geef ik de coördinaten van de hoeken van de driehoek mee met als resultaat:

Afbeeldingslocatie: http://zooi.sildo.net/inet%20files/GOT/Tekenen%20van%20border%20rond%20driehoek/1.png

Vervolgens wil ik de border van de wijzer tekenen d.m.v. de methode Graphics.DrawPolygon waaraan ik dezelfde coördinaten mee geef.
Dit geeft het gewenste resultaat:

Afbeeldingslocatie: http://zooi.sildo.net/inet%20files/GOT/Tekenen%20van%20border%20rond%20driehoek/2.png

Echter als de dikte van de border groter is dan 1 pixel dan komt de "wijs-punt" van de wijzer te hoog te liggen. Dit komt omdat de border aan de buitenkant van de driehoek getekend wordt. Zie:

Afbeeldingslocatie: http://zooi.sildo.net/inet%20files/GOT/Tekenen%20van%20border%20rond%20driehoek/3.png

Wat ik dus wil is dat de border precies binnen de cirkel valt.

Zelf geprobeerd
- Op GoT gezocht naar anderen met dit probleem.
- Op internet gezocht of het mogelijk is om de border aan de binnenkant van de driehoek te laten tekenen d.m.v. aan andere methode/overload. Niet kunnen vinden.
- Proberen uit te rekenen wat de coördinaten van de border-driehoek moeten zijn zodat de border aan de binnenkant wordt getekend. Niet gelukt i.v.m. gebrek aan kennis.

Demo code
Dit is een relevant stukje demo code waarin duidelijk het probleem naar voren komt (als je het runt ;))
C#:
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    Graphics g = e.Graphics;
    g.SmoothingMode = SmoothingMode.AntiAlias;
    
    // Kortste zijde gebruiken
    int size = ((e.ClipRectangle.Height > ClientRectangle.Width) ? ClientRectangle.Width : ClientRectangle.Height);
    
    // 30 pixels vanaf de kant.
    size -= 30;

    // Middenpunt berekeken.
    PointF center = new PointF(
        ClientRectangle.X + ClientRectangle.Width / 2F,
        ClientRectangle.Y + ClientRectangle.Height / 2F
    );

    // Rectangle waar binnen de klok getekend moet worden.
    RectangleF bounds = new RectangleF(center.X - size / 2F, center.Y - size / 2F, size, size);

    // Achtergrond cirkel tekenen.
    g.FillPie(new SolidBrush(Color.White), bounds.X, bounds.Y, bounds.Width, bounds.Height, 0, 360);

    // Radius berekenen.
    float r = (size) / 2F;

    // Het is 2 uur.
    int hours = 2;
    // Hoek op 0 of 12 uur.
    float zeroHourAngle = (float)Math.PI;
    // Hoek bij de aangegeven tijd (2 uur in dit geval).
    float angle = (float)(zeroHourAngle - ((360F / 12F * hours) / 180F * Math.PI));
    
    // De breedte van de basis van de wijzer.
    float pointerBaseWidth = 25F;

    PointF p0 = new PointF(
        (float)(center.X + (pointerBaseWidth / 2) * Math.Sin(angle - (Math.PI / 2))),
        (float)(center.Y + (pointerBaseWidth / 2) * Math.Cos(angle - (Math.PI / 2)))
    );

    PointF p1 = new PointF(
        (float)(center.X + (pointerBaseWidth / 2) * Math.Sin(angle + (Math.PI / 2))),
        (float)(center.Y + (pointerBaseWidth / 2) * Math.Cos(angle + (Math.PI / 2)))
    );
    PointF p2 = new PointF(
        (float)(center.X + r * Math.Sin(angle)),
        (float)(center.Y + r * Math.Cos(angle))
    );

    
    /* Wijzer tekenen */
    
    // Arcering
    g.FillPolygon(new SolidBrush(Color.LightGray), new PointF[] { p0, p1, p2 });
    // Border
    g.DrawPolygon(new Pen(Color.Gray, 5), new PointF[] { p0, p1, p2 });

    //g.FillPolygon(Pen, new PointF[] { p0, p1, p2 });


    /* Test lijnen */

    // Centerpunt
    float centerPointWidth = 6;
    g.FillPie(new SolidBrush(Color.Red), center.X - centerPointWidth / 2F, center.Y - centerPointWidth / 2F, centerPointWidth, centerPointWidth, 0, 360);

    g.SmoothingMode = SmoothingMode.Default;
    // Bounds
    g.DrawRectangle(new Pen(Color.Violet, 1), bounds.X, bounds.Y, bounds.Width, bounds.Height);
}


Download het Visual Studio Project


En alvast wat antwoorden om het draadje ontopic te houden :+ :
- Ja, ik wil de border dikker dan 1px.
- Ja, ik wil het persé zelf tekenen en geen bestaande library gebruiken.

Acties:
  • 0 Henk 'm!

  • Styxxy
  • Registratie: Augustus 2009
  • Laatst online: 16-09 12:57
Komt door de dikte van je pen, het ziet er naar uit dat hij niet het buitenste punt neemt bij het teken van een polygon. Even testen in C# (heb al wel kerstboompjes getekend als opdracht :P, maar dit nog niet).

Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 08:23

Reptile209

- gers -

Quick-and-dirty: ga met trial-and-error aan de slag om de optimale waarde voor Adjust te vinden waarbij de wijzer (bij een bepaalde lijndikte) precies op de juiste plek uitkomt:
C#:
1
2
3
4
PointF p2 = new PointF( 
        (float)(center.X + (r - Adjust) * Math.Sin(angle)), 
        (float)(center.Y + (r - Adjust) * Math.Cos(angle)) 
    ); 

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 10-09 11:15
Voor p2 wat van de straal afsnoepen?
C#:
1
2
3
4
PointF p2 = new PointF(
    (float)(center.X + (r - 20) * Math.Sin(angle)),
    (float)(center.Y + (r - 20) * Math.Cos(angle))
);


[Edit]
Met Reptile209 dus :)

[ Voor 8% gewijzigd door riezebosch op 21-12-2009 18:06 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

  • Styxxy
  • Registratie: Augustus 2009
  • Laatst online: 16-09 12:57
Het probleem ligt aan de manier van tekenen. Hij neemt het "midden" van de lijn als referentie, een stuk valt dus buiten je coördinaat en stuk erbinnen. Hieronder een voorbeeld.

Output
Afbeeldingslocatie: http://www.imgdumper.nl/uploads2/4b2fadb3b7a51/4b2fadb3b4b71-Untitled.png

Code
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace DrawTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            PointF p1 = new PointF((float)20, (float)20);
            PointF p2 = new PointF((float)100, (float)100);
            PointF p3 = new PointF((float)30, (float)150);

            Pen penSmal = new Pen(Color.Black, 1);
            Pen penDik = new Pen(Color.Black, 5);

            Pen penRood = new Pen(Color.Red, 1);

            Graphics g = e.Graphics;

            g.DrawPolygon(penDik, new PointF[] { p1, p2, p3 });
            g.DrawLine(penRood, p1, p2);
            g.DrawLine(penRood, p2, p3);
            g.DrawLine(penRood, p3, p1);
        }
    }
}


Je zal je coördinaten een beetje moeten aanpassen zodat de dikte van je pen wordt gecompenseerd (dus de helft van de dikte moet je van de coördinaten af doen, denk ik).

[ Voor 7% gewijzigd door Styxxy op 21-12-2009 18:07 ]


Acties:
  • 0 Henk 'm!

  • Face_-_LeSS
  • Registratie: September 2004
  • Niet online
Styxxy schreef op maandag 21 december 2009 @ 17:52:
Komt door de dikte van je pen, het ziet er naar uit dat hij niet het buitenste punt neemt bij het teken van een polygon. Even testen in C# (heb al wel kerstboompjes getekend als opdracht :P, maar dit nog niet).
Klopt, dat is precies het probleem. Ik hoop eigenlijk dat er een standaard oplossing voor is maar ik verwacht dat ik m'n wiskunde boek moet gaan afstoffen :+

[ Voor 50% gewijzigd door Face_-_LeSS op 21-12-2009 18:07 ]


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 20:58

Haan

dotnetter

Enigzins off-topic tip, maar je zou dit tekenen mooier kunnen doen in WPF dan met een Forms applicatie.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • Styxxy
  • Registratie: Augustus 2009
  • Laatst online: 16-09 12:57
Face_-_LeSS schreef op maandag 21 december 2009 @ 18:06:
[...]
Klopt, dat is precies het probleem. Ik hoop eigenlijk dat er een standaard oplossing voor is maar ik verwacht dat ik m'n wiskunde boek moet gaan afstoffen :+
Er is niet echt een standaard oplossing voor, buiten je coördinaten aanpassen (wat een leuk job voor jou gaat worden :P). Je moet namelijk je driehoek laten "krimpen", wat niet zo maar even + of - doen is ;).

Acties:
  • 0 Henk 'm!

  • Face_-_LeSS
  • Registratie: September 2004
  • Niet online
Styxxy schreef op maandag 21 december 2009 @ 18:14:
Er is niet echt een standaard oplossing voor, buiten je coördinaten aanpassen (wat een leuk job voor jou gaat worden :P). Je moet namelijk je driehoek laten "krimpen", wat niet zo maar even + of - doen is ;).
Ik was al bang dat dat het enige alternatief is. Dat wordt binnenkort even een avondje hersenskraken want ik moet precies uitrekenen wat de nieuwe straal moet zijn en het nieuwe basispunt.
Meedenkers(/mensen die wél naar school zijn geweest) welkom!

Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 08:23

Reptile209

- gers -

Ik neem toch aan dat je niet elke 2 seconden die pendikte wil gaan aanpassen? Dan is mijn snelle vieze fix een prima oplossing, hoor! Hoef je niks voor in een boek op te zoeken. Begin eens met Adjust = 20 en kijk dan of je een hogere of lagere waarde nodig hebt. Zou niet meer dan een paar pogingen moeten kosten om een goed werkende waarde te vinden.

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

Verwijderd

als andere oplossing zou je een standaard driehoek kunnen maken en deze dan gaan roteren

Acties:
  • 0 Henk 'm!

  • Styxxy
  • Registratie: Augustus 2009
  • Laatst online: 16-09 12:57
Reptile209 schreef op maandag 21 december 2009 @ 18:36:
Ik neem toch aan dat je niet elke 2 seconden die pendikte wil gaan aanpassen? Dan is mijn snelle vieze fix een prima oplossing, hoor! Hoef je niks voor in een boek op te zoeken. Begin eens met Adjust = 20 en kijk dan of je een hogere of lagere waarde nodig hebt. Zou niet meer dan een paar pogingen moeten kosten om een goed werkende waarde te vinden.
Aangezien hij een klok wilt tekenen, zal je driehoek zeker om de X aantal keren verplaatst worden. Ik weet niet direct of een vaste waarde voor adjust wel goed is, aangezien het van de positie (van de andere punten) afhangt of deze al dan niet positief of negatief is. Hetgeen de TS moet doen, is gewoon drie nieuwe basispunten bepalen, die hij dan naargelang het uur correct aanpast.
Verwijderd schreef op maandag 21 december 2009 @ 18:40:
als andere oplossing zou je een standaard driehoek kunnen maken en deze dan gaan roteren
Het probleem is eigenlijk dat je niet echt met objecten werkt, je werkt dus niet met een "driehoek" object dat je kan roteren op je canvas. Je kleurt namelijk gewoon pixels in van je canvas (Graphics). In dit geval teken je een polygon en je zegt wat de hoekpunten zijn. .NET kijkt dan welke pixels hij allemaal moet kleuren.


@TS: misschien kan je anders eens beginnen met het teken van je polygon voor 12 = 0 uur? Dan heb je eigenlijk je basispunten en kan je veel makkelijker vinden hoe je je punten moet aanpassen.

[ Voor 36% gewijzigd door Styxxy op 21-12-2009 19:08 ]


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Reptile209 schreef op maandag 21 december 2009 @ 18:36:
Ik neem toch aan dat je niet elke 2 seconden die pendikte wil gaan aanpassen? Dan is mijn snelle vieze fix een prima oplossing, hoor!
Sure. ;)
C#:
82
83
    g.DrawPolygon(new Pen(Color.Gray, 5) { Alignment = PenAlignment.Inset },
        new[] { p0, p1, p2 });

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Face_-_LeSS
  • Registratie: September 2004
  • Niet online
Yeah baby! Ik vond het al zo waarschijnlijk dat het gewoon standaard moest kunnen. Het leek me zo'n vanzelfsprekend inherent probleem bij het tekenen van randen.

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Er staat overigens wel een mogelijke gotcha in de documentatie:
The people who dreamed up the .NET API (most particularly the graphics APIs) and their documentation often make my mind boggle.

For instance, what would you think if someone said to you: “You have 5 different options. Four of these are exactly the same. The other one is sometimes the same and sometimes not (though we can’t say when or why), and you’re not allowed to choose it some of the time.”

That’s exactly what the official documentation for System.Drawing.Pen.Alignment states:

This property determines how the Pen draws closed curves and polygons. The PenAlignment enumeration specifies five values; however, only two values—Center and Inset—will change the appearance of a drawn line. Center is the default value for this property and specifies that the width of the pen is centered on the outline of the curve or polygon. A value of Inset for this property specifies that the width of the pen is inside the outline of the curve or polygon. The other three values, Right, Left, and Outset, will result in a pen that is centered.

A Pen that has its alignment set to Inset will yield unreliable results, sometimes drawing in the inset position and sometimes in the centered position. Also, an inset pen cannot be used to draw compound lines and cannot draw dashed lines with Triangle dash caps.
:X

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 08:23

Reptile209

- gers -

Styxxy schreef op maandag 21 december 2009 @ 19:02:
[...]

Aangezien hij een klok wilt tekenen, zal je driehoek zeker om de X aantal keren verplaatst worden. Ik weet niet direct of een vaste waarde voor adjust wel goed is, aangezien het van de positie (van de andere punten) afhangt of deze al dan niet positief of negatief is. Hetgeen de TS moet doen, is gewoon drie nieuwe basispunten bepalen, die hij dan naargelang het uur correct aanpast.
Okee, misschien wat te kort door de bocht opgeschreven. De punt van de wijzer wordt berekend op basis van de straal van de cirkel en de hoek van de wijzer (de tijd). Als de punt te ver doorschiet, moet je hem daarvoor corrigeren door de straal in te korten (mijn Adjust). Zodra je de waarde voor Adjust voor een bepaalde pendikte hebt, ben je klaar en kan je hem onder alle hoeken tekenen. Je hoeft daarvoor de pendikte niet aan te passen (zou ook een leuk effect kunnen zijn, maar was geen specificatie in de TS :) ).

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
Ik denk ook dat de Adjust onafhankelijk van de tijd kan zijn. Het hangt vooral af van de hoek die de 2 kruisende lijnen op de punt maken en de gebruikte LineJoin en MiterLimit; probeer maar uit anders:
C#:
82
83
    g.DrawPolygon(new Pen(Color.Gray, 5) { LineJoin = LineJoin.Bevel },
        new[] { p0, p1, p2 });

Misschien is dat nog wel mooier; de meeste wijzers zijn tenslotte niet zo scherp. Daarnaast zou ik verwachten dat de wijzer nog ietje langer is, zodat deze goed vast te maken is in het midden... ;)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten

Pagina: 1