Forum: Mikrocontroller und Digitale Elektronik Analog-Skala Zeiger zeichnen


von mike (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe eine Skala auf der ich den Verbrauch meiner Beleuchtung 
anzeigen will.
Das Problem was ich habe ist den Zeiger zu zeichnen.
mein Mittelpunkt vom Zeiger ist in X84 in Y84,die Zeigerlänge ist 
54Pixel.
Bei zb null Watt lcd_line (54,84,84,84).
Wie kann ich jetzt die einzelnen Teilstriche Berechnen.
Vielleicht kann mir einer von ihnen weiterhelfen.
Mfg

von Marvin M. (Gast)


Lesenswert?

Moin,

suche mal nach "Bresenham" - in Wikipedia gibt es unter diesem Begriff 
einen Algorithmus, mit dem man Linien zeichnen kann. Den Radius würde 
ich in diesem Fall als Tabelle hinterlegen - man kanns aber auch 
ausrechnen...

von Sven B. (scummos)


Lesenswert?

Naja, das Maximum ist ja 100W von der Skala. Bei n Watt Messwert muss 
also der Winkel gerade a = 180 * n / 100 sein. Wenn r der Radius von der 
Skala in Pixeln ist, dann ist der dazu passende Punkt auf dem Kreis 
gerade (r cos(a), r sin(a)) -- unter der Annahme, dass der Startpunkt 
des Zeigers (also der Mittelpunkt der Skala) gerade (0, 0) ist, sonst 
halt entsprechend verschieben. Wie immer halt bei einem Kreis. ;)

Grüße,
Sven

von mike (Gast)


Lesenswert?

1
Wenn ich mich nicht irre sind 0Grad=50Watt,
2
das wäre dann zb. bei 0Watt -180Grad und bei 100Watt 360Grad.
3
4
5
void leistung_analog()
6
{
7
int16_t k,x,y,d;
8
9
 for(k=0; k<100; k++)
10
 {  
11
        d=36*k;
12
        d=d/10;
13
        d=d-180;
14
15
16
  
17
  x = x + 54 * cos( d );
18
  y = y + 54 * sin( d );  
19
  
20
  bresenham_line(84,84,x,y,1);
21
  
22
}
23
24
25
}
26
27
28
29
30
31
void bresenham_line (unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2, unsigned char color)
32
{  signed short dx,dy,x,y;
33
  signed char sdx,sdy;
34
  unsigned short i,px,py;
35
36
  dx=(signed int)x2-x1;      /* the horizontal distance of the line */
37
  dy=(signed int)y2-y1;      /* the vertical distance of the line */
38
  if (dx==0)
39
    sdx=0;
40
  if (dx>0)
41
    sdx=1;
42
  else
43
  {    sdx=-1;
44
    dx=-dx;
45
  }
46
  if (dy==0)
47
    sdy=0;
48
  if (dy>0)
49
    sdy=1;
50
  else
51
  {  sdy=-1;
52
    dy=-dy;
53
  }
54
  x=dx*2;
55
  y=dy*2;
56
  px=x1;
57
  py=y1;
58
  lcd_setpixel (px,py,color);
59
60
  if (dx>=dy)       // the line is more horizontal than vertical
61
  {  for(i=0;i<dx;i++)
62
    {  y+=dy;
63
      if (y>=dx)
64
      {  y-=dx;
65
        py+=sdy;
66
      }
67
      px+=sdx;
68
      lcd_setpixel (px,py,color);
69
    }
70
  }
71
  else         // the line is more vertical than horizontal
72
  {  for(i=0;i<dy;i++)
73
    {  x+=dx;
74
      if (x>=dy)
75
      {  x-=dy;
76
        px+=sdx;
77
      }
78
      py+=sdy;
79
      lcd_setpixel (px,py,color);
80
    }
81
  }
82
}

von Sven B. (scummos)


Lesenswert?

mike schrieb:
> Wenn ich mich nicht irre sind 0Grad=50Watt,
> das wäre dann zb. bei 0Watt -180Grad und bei 100Watt 360Grad.
Nicht einmal zwei dieser drei Aussagen passen zusammen, geschweige denn 
alle. Zeichne Dir das mal auf.
Du kannst sagen 0 Watt sind -90 Grad, dann sind 50 Watt 0 Grad und 100 
Watt sind +90 Grad. Oder Du sagst 0 Watt sind 0 Grad, 50 Watt sind 90 
und 100 Watt sind 180. Geht auch.

Deine Lösung sieht ziemlich "over-engineered" aus (funktioniert sie denn 
wenigstens?). Du musst doch bloß das ausrechnen was ich gesagt habe. Die 
Koordinaten vom Zielpunkt sind (r cos(180 * n / 100) + ofs_x, r sin(180 
* n / 100) + ofs_y). Fertig. Eine Zeile.

von mike (Gast)


Lesenswert?

ich habe das nach ihren Vorschlag umgesetzt leider führt die dann immer 
zum Reset.
Könnte ich die werte auch in eine Tabelle eintragen für den Startwert 
x,y,
das währen dann 100 Werte.
Müsste ich mir alle einzeln Ausrechnen.


1
void leistung_analog()
2
{
3
int16_t x,y,r;
4
  x = 0;
5
  y = 0; 
6
  r= 54; 
7
8
 for(k=0; k<100; k++)
9
 {  
10
  lcd_line(84, 84,r* cos(180 * k / 100) + 84, r *sin(180 * k / 100) + 84,1);
11
   
12
 }
13
}

von mike (Gast)


Lesenswert?

So, ich habe nochmal mit 50 Gerechnet.
Mein Taschenrechner habe ich auf Radisnt umgestellt.

Aber leider passt dass ergebniss nicht
 x_end  59.355
 y_end  133.169

 x_end  sollte doch bei 84 bleiben
 y_end  sollte bei 55 sein

Könnte mir von ihnen mir ein wenig auf die sprünge helfen.
1
void leistung_analog()
2
{
3
 int16_t x_mitte,y_mitte,x_end,y_end;
4
 unsigned char k,r;
5
6
 k = 50;       // Skalen-Wert zb 50Watt
7
 r = 55;       // Radius = Zeigerlänge 
8
 x_mitte = 84; // Mitte x
9
 y_mitte = 84; // Mitte y
10
 
11
 x_end = x_mitte + r * cos(180 * k/ 100);  // 59.355
12
 y_end = y_mitte + r * sin(180 * k/ 100);  // 133.169
13
14
 lcd_line(x_mitte, y_mitte,x_end , y_end,1);
15
}

von Simon K. (simon) Benutzerseite


Lesenswert?

Radiant ist Bogenmaß. Das musst du auch bei den Bibliotheksfunktionen 
sin/cos benutzen. Also nix mit 180.

von mike (Gast)


Lesenswert?

Grad = 180
Bogenmaß = Grad * Pi/180
Bogenmaß = 3.14


aber so kann das doch nicht gehen oder .

könnte mir denn das von ihnen mal zeigen.
mfg
1
 x_end = x_mitte + r * cos(3.14* k/ 100);  // 59.355
2
 y_end = y_mitte + r * sin(3.14* k/ 100);  // 133.169
3
4
 lcd_line(x_mitte, y_mitte,x_end , y_end,1);

von mike (Gast)


Lesenswert?

Aber auch hier bekomme ich nicht den richtigen wert
1
void leistung_analog()
2
3
{
4
 
5
 int16_t x_mitte,y_mitte,x_end,y_end,Grad,Bogenmass  ;
6
 unsigned char k,r;
7
8
 k = 50;       // Skalen-Wert zb 50Watt
9
 r = 55;       // Radius = Zeigerlänge 
10
 x_mitte = 84; // Mitte x
11
 y_mitte = 84; // Mitte y
12
 
13
 Grad = 180 * k / 100; 
14
 Bogenmass = Grad *   M_PI/180; 
15
 
16
 lcd_line(r* cos(Bogenmass) , r* sin(Bogenmass) ,84, 84,1);
17
}

von eProfi (Gast)


Lesenswert?

Was macht das Display?
Bogenmass muss float sein, da der Wertebereich sehr klein.

von eProfi (Gast)


Lesenswert?

Du musst auch darauf achten, dass in Deiner Skala der Nullpunkt links 
liegt, aber bei den trigonometrischen Funtkionen liegt er rechts.

Außerdem kann sein, dass positive x nach rechts zeigen, aber positive 
y-Werte nach unten und nicht wie in der Mathe üblich nach oben.


#define PI100 (3.1415926 / 100)
#define XO 84
#define YO 84

               minus
                 |
                 |
                 V
 x_end = x_mitte - r * cos(PI100 * k);
 y_end = y_mitte - r * sin(PI100 * k);



Wenn Du nur Teilstriche haben willst, brauchst Du zwei Radien:
 single cosi = cos(PI100 * k);
 single sinu = sin(PI100 * k);
 x1 = XO - r1 * cosi;
 y1 = YO - r1 * sinu;
 x2 = XO - r2 * cosi;
 y2 = YO - r2 * sinu;
 lcd_line(x1, y1, x2 , y2, 1);


Kannst Du auch in eine Zeile schreiben:
 lcd_line(XO - r1 * cosi,YO - r1 * sinu,XO - r2 * cosi,YO - r2 * sinu);

von mike (Gast)


Lesenswert?

eProfi schrieb:
> #define PI100 (3.1415926 / 100)
> #define XO 84
> #define YO 84
>
>                minus
>                  |
>                  |
>                  V
>  x_end = x_mitte - r * cos(PI100 * k);
>  y_end = y_mitte - r * sin(PI100 * k);

> Wenn Du nur Teilstriche haben willst, brauchst Du zwei Radien:
>  single cosi = cos(PI100 * k);
>  single sinu = sin(PI100 * k);
>  x1 = XO - r1 * cosi;
>  y1 = YO - r1 * sinu;
>  x2 = XO - r2 * cosi;
>  y2 = YO - r2 * sinu;
>  lcd_line(x1, y1, x2 , y2, 1);

> Kannst Du auch in eine Zeile schreiben:
>  lcd_line(XO - r1 * cosi,YO - r1 * sinu,XO - r2 * cosi,YO - r2 * sinu);

Vielen Dank hoffentlich bekomm ich das auch hin.
r1,r2 Währe ja mein Radius.

von Karl H. (kbuchegg)


Lesenswert?

Das was du suchst, ist nichts anderes als die Anwendung der 
Trigonometrie in einem rechtwinkeligen Dreieck.

gegeben ein rechtwinkeliges Dreieck mit der Hypothenuse (die längere 
Seite) R, wobei R im Winkel Alpha zur X-Achse liegt.
Gefragt sind die Längen der beiden anderen Seiten.
Die eine hat Seitenlänge R*cos(Alpha)
die andere Seite hat die Länge R*sin(Alpha)

D.h. dies sind auch gleichzeitig die Koordinaten des Punktes, an dem 
sich die Hypothenuse mit einem Kreis mit Radius R schneidet.

Alles andere sind jetzt nur noch Anpassungen, weil dein Kreis eben 
seinen Mittelpunkt nicht bei den Koordinaten 0/0 hat, sondern dazu 
verschoben ist. D.h. auf alle Punkte wird noch die Verschiebung dieses 
Mittelpunkts mit aufgerechnet.
Und dann hast du natürlich noch den Fall, dass dein 0-Winkel eben nicht 
auf der X-Achse nach rechts verläuft, sondern bei dir eben anders liegt. 
Auch gibt es noch den Fall, dass die Y-Koordinate nicht nach oben läuft, 
so wie in der Mathematik üblich, sondern nach unten. Daraus resultieren 
ein paar Koordinaten Umrechnungen, in dem anstelle Addition eine 
Subtraktion erfolgt.

Aber das Grundprinzip sind immer noch die Punktkoordinaten, die sich aus 
den Seitenlängen im rechtwinkeligen Dreieck durch Anwendung von Sinus 
und Cosinus ergeben.

(Und nicht vergessen: Winkel werden in Radianten angegeben und nicht in 
Grad. Ein Vollkreis hat 2*PI Radianten, 180° sind daher PI Radianten.)

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

MIst, Zeichnung vergessen

von Christen (Gast)


Lesenswert?

Brauchst kein sin/cos Funktionen. Leg dir eine Tabelle mit 50 Werten an 
(viertel Kreis reicht). Wenn dein Display 100 Werte pro Halbkreis 
darstellen kann Respekt (je nach Gaughe Größe). Zu den Werten kommst du 
am Besten mit einem Grafikprogramm. ZB einen Eagle Sheet, Raster 1mm (= 
Pixelgröße), Halbkreis zeichen,Messpunkt setzen und du hast deine 
Koordinaten, wenn du mit der Maus über den gew. Punkt fährst.So kannst 
du jede beliebige Grafik vermessen.
Hab gerade damit eine Analoguhr kreiert. Mit den Cos/Sin Funktionen hat 
alles zu lange gedauert (Zeiger Überlappungen ect.) und hat riesigen 
Code gekostet.


SG Christen

von Sven B. (scummos)


Lesenswert?

"Hm, jetzt hätte ich die Striche gern ein bisschen länger... Mist, 
nochmal alles von vorne machen!"

Dieses Verfahren ist meiner Meinung nach nicht besonders toll... wenn es 
unbedingt schnell sein soll, kann man die Werte auch nach dem ersten 
Zeichnen in einen Cache schreiben.

von Ingo (Gast)


Lesenswert?

Ich würde hier auch auf eine LUT zurück greifen, das bissel Speicher...

von Sven B. (scummos)


Lesenswert?

Am besten eine, die programmatisch generiert ist ;)
Notfalls auch auf einem (größeren) Rechner.
Python eignet sich hervorragend, um sowas schnell zusammenzuhacken.

von mike (Gast)


Lesenswert?

ich habe mal noch eine Frage ,
Die Funktion wird aufgerufen wert hat jetz 512,zeiger wird gezeichnet, 
soweit geht alles.
nun wird die Funktion ein zweites mal aufgerufen wert hat jetz 130, nun 
wollte ich den zeiger vom ersten wert weis neuzeichnen und danach den 
zeiger mit dem neuen Wert zeichnen, anstatt jedes mal das BMP file neu 
laden.

Leider geht das nicht so wie ichs mir dachte
vielleicht kann mir wer weiterhelfen

1
//Global
2
int16_t x_m_alt,y_m_alt,x_e_alt,y_e_alt;
3
 x_m_alt = 85; // Mitte x
4
 y_m_alt = 82; // Mitte y
5
6
void leistung_analog_w(int xpos, int ypos,uint16_t wert,uint8_t mode)
7
{
8
 #define PI100 (3.1415926 / 100)
9
10
11
 uint16_t  ad;
12
 int16_t x_m,y_m,x_e,y_e;
13
 unsigned char r;
14
 float wertk;  
15
16
 
17
 r = 55;       // Radius = Zeigerlänge 
18
 x_m = 85; // Mitte x
19
 y_m = 82; // Mitte y
20
 
21
 // BMP Zeichen 
22
 // Chdir("skalen");    // ORDNER SKALEN  ÖFFNEN
23
 // LoadGraphDisplay("skalew.bmp",xpos,ypos,0); //BMP NORMAL
24
 // Chdir("..");        //Ordner Schliessen      
25
     
26
 // alten Zeiger Zeichnen beim erneuten Funktionsaufruf
27
 lcd_line(x_m_alt + xpos, y_m_alt + ypos,x_e_alt +  xpos, y_e_alt + ypos,1);
28
29
 wertk= (wert*100.0)/ 1023;
30
31
 x_e = x_m - r * cos(PI100 * wertk);
32
 y_e = y_m - r * sin(PI100 * wertk);
33
34
 // neuen Zeiger Zeichnen
35
 lcd_line(x_m + xpos, y_m + ypos,x_e +  xpos, y_e + ypos,1);
36
37
 // kopiere Werte in alt
38
 x_e_alt = x_e;
39
 y_e_alt = y_m;
40
}

von MaWin (Gast)


Lesenswert?

Sollte aber gehen wenn leistung_analog_w jedesmal mit identischem xpos 
und ypos aufgerufen werden denn xm und xm_alt und ym und ym_alt sind 
zufälligerweise gleich.
Bloss: Woher weiss der erste Aufruf von lcd_linie dass sie weiss 
zeichnen soll? Der letzte Parameter ist ebenfalls 1, zeichnet die 
Funktion im XOR Mode?

von Karl H. (kbuchegg)


Lesenswert?

mike schrieb:


>  // kopiere Werte in alt
>  x_e_alt = x_e;
>  y_e_alt = y_m;

Das sollte wohl y_e sein und nicht y_m

von mike (Gast)


Lesenswert?

So gehts wunderbar,
Es gibt sicherlich noch eine ander Vorgehensweise.
1
//Global
2
  int16_t x_m_alt,y_m_alt,x_e_alt,y_e_alt;
3
  x_m_alt = 85; // Mitte x
4
  y_m_alt = 82; // Mitte y
5
 
6
void leistung_analog_w(int xpos, int ypos,uint16_t wert,uint8_t mode)
7
 {
8
  #define PI100 (3.1415926 / 100)
9
10
  uint16_t  ad;
11
  int16_t x_m,y_m,x_e,y_e;
12
  unsigned char r;
13
  float wertk;  
14
15
  r = 55;       // Radius = Zeigerlänge 
16
  x_m = 85; // Mitte x
17
  y_m = 82; // Mitte y
18
  
19
 // BMP Zeichen 
20
  // Chdir("skalen");    // ORDNER SKALEN  ÖFFNEN
21
 // LoadGraphDisplay("skalew.bmp",xpos,ypos,0); //BMP NORMAL
22
 // Chdir("..");        //Ordner Schliessen      
23
      
24
 // alten Zeiger Zeichnen beim erneuten Funktionsaufruf 
25
  lcd_line(x_m_alt + xpos, y_m_alt + ypos,x_e_alt +  xpos, y_e_alt + ypos,0);
26
27
  wertk= (wert*100.0)/ 1023;
28
29
  x_e = x_m - r * cos(PI100 * wertk);
30
  y_e = y_m - r * sin(PI100 * wertk);
31
32
// neuen Zeiger Zeichnen
33
  lcd_line(x_m + xpos, y_m + ypos,x_e +  xpos, y_e + ypos,1);
34
35
  // kopiere Werte in alt
36
37
  x_e_alt = x_m_alt - r * cos(PI100 * wertk);
38
  y_e_alt = y_m_alt - r * sin(PI100 * wertk);
39
}

von Karl H. (kbuchegg)


Lesenswert?

mike schrieb:
> So gehts wunderbar,

Wenn es einem egal ist, dass man dieselbe Berechnung 2 mal ohne guten 
Grund macht, dann: ja

da x_m_alt/y_m_alt sowieso nie andere Werte bekommen als x_m/y_m würde 
ich dieses Variablenpärchen gleich mal eliminieren. In Wirklichkeit 
braucht die keiner. x_m/y_m alleine tut es auch.

damit bleibt hier

>   // kopiere Werte in alt
>
>   x_e_alt = x_m_alt - r * cos(PI100 * wertk);
>   y_e_alt = y_m_alt - r * sin(PI100 * wertk);

übrig

   x_e_alt = x_m - r * cos(PI100 * wertk);
   y_e_alt = y_m - r * sin(PI100 * wertk);


und wenn man das mit dem Code 3 Zeilen drüber vergleicht

  x_e = x_m - r * cos(PI100 * wertk);
  y_e = y_m - r * sin(PI100 * wertk);

dann kommt man drauf: das ist genau das gleiche.
Wozu also neu durch den sin/cos durchjagen?
Ein

  x_e_alt = x_e;
  y_e_alt = y_e;

tuts auch.

Wenn du Strukturverbesserungen in deinen Code bringen willst, dann denk 
darüber nach, dass ja eigentlich x und y immer zusammengehören und eine 
logische Einheit, eine 'Koordinate' bilden.

Ergo
1
struct coord
2
{
3
  int16_t x;
4
  int16_y y;
5
};
6
7
8
struct coord center = { 85, 82 };
9
struct coord lastDraw;
10
11
#define PI100 (3.1415926 / 100)
12
#define NEEDLE_LENGTH    55
13
14
15
void updateNeedle( uint16_t newValue, uint8_t mode )
16
{
17
  float angleValue;
18
  struct coord newDraw;
19
20
  angleValue = ( newValue * 100.0 / 1023 ) * PI100;
21
22
  newDraw.x = center.x - NEEDLE_LENGTH * cos(angleValue);
23
  newDraw.y = center.y - NEEDLE_LENGTH * sin(angleValue);
24
25
  //
26
  // gibts optisch überhaupt was zu tun?
27
  //
28
  if( newDraw.x != lastDraw.x || newDraw.y != lastDraw.y )
29
  {
30
    // alten Zeiger weg
31
    lcd_line( center.x, center.y,
32
              lastDraw.x, lastDraw.y,
33
              0 );
34
35
    // neuen Zeiger hin
36
    lcd_line( center.x, center.y,
37
              newDraw.x, newDraw.y,
38
              1 );
39
40
    lastDraw = newDraw;
41
  }
42
}

von mike (Gast)


Lesenswert?

Das beispiel von Karl Heinz Buchegger hier zeigt das haut hin,
Nun meine Frage ich wollte nun de Zeiger etwas veränder breiter machen, 
ich habe erstmal in y 1pixel dazugerechnet aber das geht ja nur  null 
Grad und bei 180Grad .
Ich wollte gern das der Zeiger ungefähr so aussieht wie auf dm Bild hier
http://i01.i.aliimg.com/img/pb/622/182/379/379182622_737.jpg

Vielleicht könnte mir einer von ihnen weiterhelfen wie ich das richtig 
berechne
Mfg

von Karl H. (kbuchegg)


Lesenswert?

mike schrieb:

> Vielleicht könnte mir einer von ihnen weiterhelfen wie ich das richtig
> berechne

Jetzt beginnt der Bereich, in dem Computergraphik wirklich anfängt.
Am sinnvollsten beschäftigt man sich jetzt bereits mit Transformationen. 
Keine Angst, so wild ist das im 2D noch nicht.

Wenn ich vormittags über ein wenig Zeit hab, dann bereite ich dir was 
vor.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Wenn ich vormittags über ein wenig Zeit hab, dann bereite ich dir was
> vor.

Sorry. Ich hatte tagsüber wenig Zeit einen Artikel zu schreiben, der ein 
wenig Vorbereitung bedarf. Ich werde hier auch wahrscheinlich mehrere 
Postings folgen lassen, weil ich denke, dass es ohne Zeichnungen nicht 
gehen wird und ich möchte die Zeichnungen jeweils in einen Zusammenhang 
stellen. Ich geb Bescheid, wenn die 'Serie' abgeschlossen ist.


Die gute (oder schlechte) Nachricht zuerst:
Nach reiflicher Überlegung, denke ich, dass es nicht notwendig ist, dich 
in die tieferen Geheimnisse von Transformationen einzuweihen. Für deine 
Zwecke, kann das recht einfach gehalten werden. Aber ein bisschen Mathe, 
im speziellen Trigonometrie, muss sein. Ohne geht es nun mal nicht.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Lesenswert?

Aber zuerst mal ein bischen Notation um die Sache ins Rollen zu bringen. 
Einiges davon wird im Endeffekt nicht gebraucht werden, aber es schadet 
nicht, uns erst mal in der Sprache abzustimmen.

Man kann einen beliebigen Punkt um eine bestimmte Distanz verschieben, 
indem man einfach zu seinen Koordinaten die Distanzen addiert
1
      x' = x + dx
2
      y' = y + dy
Diese beiden Gleichung besagen, dass ein Punkt mit den Koordinaten x und 
y um eine Distanz dx;dy verschoben wird und man dadurch einen neuen 
Punkt mit den Koordinaten x' und y' erhält.


Um die Notation etwas zu vereinfachen, definieren wir einen 
Spaltenvektor als so eine Einheit von x und y. Der Vektor P wäre dann 
der Punkt
1
        | x |
2
    P = |   |              ( die | sind einfach nur senkrechte Striche
3
        | y |                wie sie in Mathe üblich sind. Ich werde sie
4
                             in Bälde einfach weglassen )
die Distanz, um die verschoben wird, kann man ebenfalls als Vektor 
anschreiben
1
        | dx |
2
    T = |    |
3
        | dy |
und das Ergebnis der Translation ist dann der neue Punkt, als Vektor 
ausgedrückt also der
1
        | x' |
2
   P' = |    |
3
        | y' |
Man kann also die beiden obigen Gleichungen
1
   x' = x + dx
2
   y' = y + dy
auch als Vektorgleichung aufschreiben:
1
    P' = P + T
bis jetzt ist noch nichts aufregendes passiert. Einfach nur im 
Hinterkopf halten, dass Grossbuchstaben immer für Vektoren stehen, die 
(weil wir uns ja im 2D bewegen) aus jeweils einer x Komponente und einer 
y Komponente bestehen. Werden 2 Vektoren addiert, dann werden sie 
komponentenweise addiert. Um also P + T zu berechnen, rechnet man P.x 
(das steht für die x-Komponente des Vektors P), also P.x + T.x bzw. P.y 
+ T.y und das jeweilige Ergebnis geht nach P'.x bzw. P'.y

Ich denke, bis hier her ist das noch nicht weiter schwer. Auch wenn da 
jetzt Begriffe wie Vektoren gefallen sind, ist das einfach nur eine 
Notation, mehr nicht.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Der springende Punkt ist nun, dass man eine Figur als ganzes verschiebt, 
der Translation T unterwirft, indem man die Transformation
1
    P' = P + T
einfach auf jeden Punkt der Figur anwendet, siehe Bild.

Da diese Figur aus Linien besteht, genügt es dabei, nur die Endpunkte 
der Linien zu transformieren. Eine gerade Linie ist auch nach der 
Transformation wieder eine gerade Linie.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Rotationen

Ein einzelner Punkt kann um den Ursprung des Koordinatensystems um den 
Winkel a gedreht werden, indem die Transformation
1
     x' = x * cos(a) - y * sin(a)
2
     y' = x * sin(a) + y * cos(a)
anwendet.
Man kann diese Formeln relativ leicht aus der Trigonometrie am 
rechtwinkeligen Dreieck herleiten, ich spars mir aber, da es nicht viel 
zur Sache beiträgt. Bei Bedarf ist das aber mit einer Zeichnung schnell 
einsichtig, warum das so ist.

Wieder kann man diese Transformation in Vektor/Matrix-form schreiben als
1
    | x' |     | cos(a)  -sin(a) |   | x |
2
    |    |  =  |                 | * |   |
3
    | y' |     | sin(a)   cos(a) |   | y |
oder eben als Vektorgleichung
1
     P' = R * P
wobei das * die Matrixmultiplikation darstellt. Also alle die 
Komponenten der jeder Zeile der Matrix mit der jeweiligen Komponente des 
Vektors multiplizieren und die Summe drüber bilden.

(die erste Zeile der Matrix lautet   cos(a)  - sin(a) , die erste (und 
einzige Spalte) der zweiten Matrix/Vektors lautet  x  y, daher cos(a) 
mal x  bzw. -sin(a) mal y; und die Summe der beiden Teilausdrücke ergibt 
die erste Komponente des Ergebnisses. Vergleichen mit der Ausgangsformel 
zeigt: passt, kommt genau dasselbe raus.
Hört sich schlimmer an als es ist, nach 2 mal selber auf dem Papier 
machen, hat man den Dreh raus)

Auch hier gilt wieder:
Um ein Objekt als ganzes zu transformieren, unterwirft man einfach jeden 
einzelnen Punkt des Objektes der Transformation und in unserem Falle 
genügt es dazu, einfach nur die Endpunkte der Linien zu betrachten

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Die zuletzt eingeführte Rotation hat ein kleines 'Problem'. Sie dreht 
immer um den Ursprung des Koordinatensystems.
Kann man degegen etwas machen?
Was tun, wenn ich nicht um den 0-Punkt sondern um einen bestimmten Punkt 
Q drehen möchte?

Nun ganz einfach.
Mittels einer Transformation, die nichts anderes tut als Q in den 
Ursprung des Koordinatensystems zu verschieben, kann man ja den 
Drehpunkt in den Ursprung bringen. Danach dreht man um den gewünschten 
Winkel und verschiebt das Ergebnis wieder zurück, macht also die 
ursprüngliche Verschiebung wieder rückgängig.

Möchte man also um den Punkt/Vektor Q eine Drehung um den Winkel a 
ausführen, so berechnet sich der jeweils neue Punkt P' zu
1
    P' = R * ( P - Q ) + Q
P - Q verschiebt den Q in den Ursprung und wendet dieselbe 
Transformation auf P an.
Die Multiplikation mit R dreht den Punkt rund um den Ursprung
Und die Addition von Q hebt schliesslich die Verschiebung in den 
Ursprung wieder auf.

Man kann nun durch weitere mathematische Operationen all die 
Einzeloperationen zu einer einzigen Matrixmultiplikation zusammenfassen, 
ich denke aber, dass dies in deinem Fall nicht notwendig sein wird. Bei 
deinen 3 Operationen kann man die auch ganz gut hintereinander 
ausführen.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Soweit so gut.

Wenn du also deinen Zeiger nicht einfach nur als Linie auffasst, sondern 
als Polygon, also eine Abfolge von Linien, die zb so aussehen (Bild)

(du brauchst nur die Eckpunkte der Linien)

dann kannst du mit dem bisherigen Rüstzeug diesen Zeiger durch 
Kombinationen von Transformationen und Rotationen an jedem beliebigen 
Ort und in jeder beliebigen Orientierung zeichnen lassen, einfach nur 
indem du entsprechende Verschiebungen bzw. Drehungen miteinander 
kombinierst.

Besonders einfach wird die ganze Sache, wenn du deinen Zeiger von vorne 
herein gleich mal so ins Koordinatensystem legst (die Koordinaten der 
Endpunkte festlegst), dass der gewünschte Drehpunkt des Zeigers von 
vorne herein im Koordinatenursprung zu liegen kommt. Wenn nicht, dann 
ist das auch kein Beinbruch, du weißt ja jetzt, wie man um jeden 
beliebigen Punkt drehen kann.

Hast du dann alle Eckpunkte deiner Zeigerfigur entsprechend 
transformiert, dann verbindest du die so transformierten Eckpunkte 
wieder durch Linien und lässt sie dir ausgeben.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Lesenswert?

Das heißt jetzt für dich.
Angenommen du hast deinen Zeiger definiert als
1
struct coord
2
{
3
  int16_t x;
4
  int16_y y;
5
};
6
7
struct coord Zeiger[] = 
8
{
9
   { -10,  0 },
10
   {   0, -5 },
11
   {  55,  0 },
12
   {   0,  5 }
13
};
(sollen die Koordinaten des weiter oben blau gemalten Zeigers sein.)


dann genügt es in deinem Fall, jeden dieser Punkt einfach um den 
Ursprung zu drehen
1
struct tmp[4];
2
3
  cosAlpha = cos( .... );
4
  sinAlpha = sin( .... );
5
6
  for( i = 0; i < 4; i++ )
7
  {
8
    tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
9
    tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;
10
  }
und danach an das von die angedachte Zentrum zu verschieben
1
  for( i = 0; i < 4; i++ )
2
  {
3
    tmp[i].x += center.x;
4
    tmp[i].y += center.y;
5
  }
um damit die Koordinaten der verschobenen und gedrehten Punkte zu 
bekommen, so dass du nur noch entsprechende Linien ausgeben musst
1
  for( i = 0; i < 3; i++ )
2
  {
3
    lcd_line( tmp[i].x,   tmp[i].y,
4
              tmp[i+1].x, tmp[i+1].y );
5
  }
6
  lcd_line( tmp[3].x, tmp[3].y, tmp[0].x, tmp[0].y );

um damit den Zeiger an der gewünschten Position und in der gewünschten 
Orientierung zu sehen. Und wenn du dann den Winkel veränderst, dann 
sollte sich eigentlich der Zeiger um seinen Drehpunkt drehen (wenn du 
den Ausradier- / Hinmal- Mechanismus noch richtig bedienst).

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Lesenswert?

So, das wars.
Ich denke, das sollte fürs erste ausreichend sein, um dir das unbedingt 
notwendig Rüstzeug zu geben, um deine ersten Schritte in die Welt der 
Computergrafik zu gehen.
Probier ein bischen rum, lass dich nicht frustrieren, arbeite in kleinen 
Schritten, dann kann nicht viel schief gehen.


Von da weg öffnet sich das weite Feld der Computergrafik und man kann da 
noch eine Menge anderer Dinge ausprobieren und sich überlegen. Aber fürs 
erste braucht es für deine Zwecke nicht mehr als diese einfachen 
Konzepte

* ein Polygon wird beschrieben durch eine Abfolge von Punkten
* Transformation
* Rotation
* man kann Transformationen und Rotationen auch hintereinander ausführen
* ein Polygon wird transformiert, indem man seine Eckpunkte durch alle
  Transformationen schleust.

von Falk B. (falk)


Lesenswert?

@ Karl Heinz Buchegger (kbuchegg) (Moderator)

Schreib doch lieber ein Buch! Oder wenigstens einen Artikel. Denn sonst 
ist deine Arbeit an nur einen Fragesteller "verschwendet".

von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner schrieb:
> @ Karl Heinz Buchegger (kbuchegg) (Moderator)
>
> Schreib doch lieber ein Buch! Oder wenigstens einen Artikel.

Hab ich mir sogar überlegt, ob ich nicht im Wiki einen Artikel dazu 
mache. Aber da muss ich dann noch weiter ausholen :-)

von oszi40 (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber da muss ich dann noch weiter ausholen

Deine Zeiger werden wird noch viele Freunde finden, da lohnt sich schon 
ein kurzer Artikel mit Verweis auf diese Diskussion.

Hinzuzufügen wäre noch, das bei Druck von Skalen gelegentlich größere 
Ungenauigkeiten durch Druckertreiber und mechanische Druckerprobleme 
auftreten. Deshalb zur Probe immer erst Quadrat 100x100 mm drucken und 
messen!

von M. B. (majo)


Lesenswert?

Also ich fände einen Artikel zu dem Thema echt gut.
Ist wirklich klasse erklärt, Karl Heinz.

von 900ss (900ss)


Lesenswert?

Karl Heinz Buchegger schrieb:
> So, das wars.
> Ich denke, das sollte fürs erste ausreichend sein, um dir das unbedingt
> notwendig Rüstzeug zu geben, um deine ersten Schritte in die Welt der
> Computergrafik zu gehen.

Toll! Aber warum erst jetzt? ;-)

Ich habe mir diese Methoden mühsam im Netz zusammensuchen müssen als ich 
mir das Ziffernblatt + Zeiger meiner Scopeuhr 
(Beitrag "Re: Zeigt her Eure Kunstwerke !") 
programmiert habe.

Ich finde deine Erklärung super und gut verständlich.

Und ich habe es ziemlich genauso gelöst wie du es hier beschrieben hast. 
Das freut mich jetzt, ich habe überhaupt keine Ahnung von 
Computergrafik. Also hab ich nicht soviel Fehler gemacht. :) Ich hätte 
das gerne so als "Kochrezept" irgendwo haben wollen, es war schon etwas 
mühsam überall die Schnipsel (Bresenham, Translation, Rotation u.s.w.) 
zusammenzusuchen. Na ja, dümmer bin ich so auch nicht geworden. So gibt 
es hier jetzt eine prima Erklärung.

Aber da einen Artikel von zu machen, dass wäre schon prima Karl-Heinz. 
Du kannst gut erklären. Danke im Nachhinein.

von mike (Gast)


Lesenswert?

Hallo Karl Heinz Buchegger,
Vielen vielen Dank für diese sehr ausführliche erklärung.
ich habe aber noch ein Problem und zwar beim Compilieren.
Bekomme das nicht Compilliert

Vielleicht können sie mir weiterhelfen
MFG

1
Aufgerufen wird updateNeedle( 512);
2
 
3
struct coord
4
{
5
  int16_t x;
6
  int16_y y;
7
};
8
9
struct coord Zeiger[] = 
10
{
11
   { -10,  0 },
12
   {   0, -5 },
13
   {  55,  0 },
14
   {   0,  5 }
15
};
16
17
18
struct coord center = { 85, 82 };
19
struct coord lastDraw;
20
21
#define PI100 (3.1415926 / 100)
22
#define NEEDLE_LENGTH    55
23
24
25
26
27
void updateNeedle( uint16_t newValue )
28
{
29
30
  int i; 
31
  float angleValue;
32
  struct coord newDraw;
33
34
  angleValue = ( newValue * 100.0 / 1023 ) * PI100;
35
36
37
  cosAlpha = cos( angleValue );
38
  sinAlpha = sin( angleValue );
39
40
  for( i = 0; i < 4; i++ )
41
  {
42
    tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
43
    tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;
44
  }
45
46
// und danach an das von die angedachte Zentrum zu verschieben
47
48
  for( i = 0; i < 4; i++ )
49
  {
50
    tmp[i].x += center.x;
51
    tmp[i].y += center.y;
52
  }
53
54
// um damit die Koordinaten der verschobenen und gedrehten Punkte zu 
55
// bekommen, so dass du nur noch entsprechende Linien ausgeben musst
56
57
  for( i = 0; i < 3; i++ )
58
  {
59
    lcd_line( tmp[i].x,   tmp[i].y,
60
              tmp[i+1].x, tmp[i+1].y );
61
  }
62
  lcd_line( tmp[3].x, tmp[3].y, tmp[0].x, tmp[0].y );
63
64
65
}

von Stefan (Gast)


Lesenswert?

Das ist eine super Übungsaufgabe für einen Programmierkurs.

von Yalu X. (yalu) (Moderator)


Lesenswert?

mike schrieb:
> Vielen vielen Dank für diese sehr ausführliche erklärung.
> ich habe aber noch ein Problem und zwar beim Compilieren.
> Bekomme das nicht Compilliert

Wie äußert sich das?

-> Der Compiler liefert Fehlermeldungen.

Und was sind das für Fehlermeldungen?

-> Undeklarierte Variablen, Funktionen und Datentypen.

Und was tut man dagegen?

-> Tippfehler beseitigen und die entsprechenden Variablen an geeigneter
   Stelle deklarieren und die passenden Headerfiles mit den Funktions-
   und Typdeklarationen includen.

Ansonsten ist das Programm nämlich korrekt.

von mike (Gast)


Lesenswert?

So  ich bekomme Trotzdem noch eine Fehlermeldung beim compillieren
Fehlermeldung:
 error: 'tmp' undeclared (first use in this function)
 error: (Each undeclared identifier is reported only once
 error: for each function it appears in.)
1
Aufgerufen wird updateNeedle( 512);
2
 
3
struct coord
4
{
5
  int16_t x;
6
  int16_t y;
7
};
8
9
struct coord Zeiger[] = 
10
{
11
   { -10,  0 },
12
   {   0, -5 },
13
   {  55,  0 },
14
   {   0,  5 }
15
};
16
17
18
uint8_t draw_old= 0;
19
20
21
struct coord center = { 97, 98 };
22
 struct tmp[4];
23
24
25
26
void updateNeedle( uint16_t newValue )
27
{
28
29
30
31
32
  int i;
33
  float angleValue;
34
  float cosAlpha;
35
  float sinAlpha;
36
37
38
    
39
40
  angleValue = ( newValue * 100.0 / 1023 ) * PI100;
41
42
43
  cosAlpha = cos( angleValue );
44
  sinAlpha = sin( angleValue );
45
46
  for( i = 0; i < 4; i++ )
47
  {
48
    tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
49
    tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;
50
  }
51
52
//und danach an das von die angedachte Zentrum zu verschieben
53
54
  for( i = 0; i < 4; i++ )
55
  {
56
    tmp[i].x += center.x;
57
    tmp[i].y += center.y;
58
  }
59
60
//um damit die Koordinaten der verschobenen und gedrehten Punkte zu 
61
//bekommen, so dass du nur noch entsprechende Linien ausgeben musst
62
63
  for( i = 0; i < 3; i++ )
64
  {
65
    lcd_line( tmp[i].x,   tmp[i].y,
66
              tmp[i+1].x, tmp[i+1].y );
67
  }
68
  lcd_line( tmp[3].x, tmp[3].y, tmp[0].x, tmp[0].y );
69
70
71
}

von Yalu X. (yalu) (Moderator)


Lesenswert?

mike schrieb:
> So  ich bekomme Trotzdem noch eine Fehlermeldung beim compillieren
> Fehlermeldung:
>  error: 'tmp' undeclared (first use in this function)

Ist das die einzige Meldung?

Hier sollte nämlich auch noch eine ausgegeben werden, weil der
Struct-Name fehlt:
1
 struct tmp[4];

Ändere diese Zeile mal in
1
struct coord tmp[4];

von andreas (Gast)


Lesenswert?

Hi

Yalu X. schrieb:
> Ändere diese Zeile mal in
> struct coord tmp[4];
Denzeiger zeichnen das geht erstma

So nun sollte aber der alte Zeiger bevor der neue Gezeichnet wird
Überschriben werden aber leider geht das so nicht wie ich mir das dachte

mfg





1
Aufgerufen wird updateNeedle( 512);
2
 
3
struct coord
4
{
5
  int16_t x;
6
  int16_t y;
7
};
8
9
struct coord Zeiger[] = 
10
{
11
   { -10,  0 },
12
   {   0, -5 },
13
   {  55,  0 },
14
   {   0,  5 }
15
};
16
struct coord lastDraw[4];
17
18
uint8_t draw_old= 0;
19
20
21
struct coord center = { 97, 98 };
22
struct coord tmp[4];
23
24
25
26
void updateNeedle( uint16_t newValue )
27
{
28
29
30
31
32
  int i;
33
  float angleValue;
34
  float cosAlpha;
35
  float sinAlpha;
36
37
38
    
39
40
  angleValue = ( newValue * 100.0 / 1023 ) * PI100;
41
42
43
  cosAlpha = cos( angleValue );
44
  sinAlpha = sin( angleValue );
45
46
47
//erstmal alten Zeiger weg
48
  for( i = 0; i < 3; i++ )
49
  {
50
    lcd_line(lastDraw[i].x,lastDraw[i].y,lastDraw[i+1].x,lastDraw[i+1].y ,0) ;
51
    
52
  }
53
 lcd_line( lastDraw[3].x, lastDraw[3].y,lastDraw[0].x,lastDraw[0].y,0) ;
54
55
56
//neuen zeiger zeichnen
57
  for( i = 0; i < 4; i++ )
58
  {
59
    tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
60
    tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;
61
  }
62
63
//und danach an das von die angedachte Zentrum zu verschieben
64
65
  for( i = 0; i < 4; i++ )
66
  {
67
    tmp[i].x += center.x;
68
    tmp[i].y += center.y;
69
  }
70
71
//um damit die Koordinaten der verschobenen und gedrehten Punkte zu 
72
//bekommen, so dass du nur noch entsprechende Linien ausgeben musst
73
74
  for( i = 0; i < 3; i++ )
75
  {
76
    lcd_line( tmp[i].x,   tmp[i].y,
77
              tmp[i+1].x, tmp[i+1].y,1 );
78
  lastDraw[i].x,lastDraw[i].y,lastDraw[i+1].x, lastDraw[i+1].y  ;
79
}
80
  lcd_line( tmp[3].x, tmp[3].y, tmp[0].x, tmp[0].y,1 );
81
82
  //alten zeiger merken
83
  lastDraw[3].x, lastDraw[3].y, lastDraw[0].x, lastDraw[0].y ;
84
85
86
}

von Karl H. (kbuchegg)


Lesenswert?

andreas schrieb:
> Hi
>
> Yalu X. schrieb:
>> Ändere diese Zeile mal in
>> struct coord tmp[4];
> Denzeiger zeichnen das geht erstma
>
> So nun sollte aber der alte Zeiger bevor der neue Gezeichnet wird
> Überschriben werden aber leider geht das so nicht wie ich mir das dachte

Andreas, ich will dir nicht zu nahe treten.
Aber du musst schon erst  mal C lernen, sonst wird das nichts.

Wenn du bei deinem Auto selbst das Motoröl wechseln willst, und man dir 
erst mal erklären muss, was ein Schraubenschlüssel ist, dann wird dir 
jeder SChrauber sagen: Weißt was, lern erst mal ein wenig mit deinem 
Werkzeug umzugehen und dann kannst wieder kommen.

Wenn du von deiner Programmierprache noch nicht mal 10% des 
Sprachumfangs beherrscht (und selbst das nur so lala), dann hat es 
keinen Sinn, wenn wir uns über weitergehende Programmiertechniken 
unterhalten. Ich bin sicher kein Unmensch und schreib auch mal für 
andere Code, aber er gibt auch eine Grenze an der ich sage: sorry. 
Entweder du lernst das, oder du bezahlst mich dafür, dass ich das 
gelernt habe. Schliesslich verdiene ich meine Brötchen damit und muss 
davon leben. Ich hab nicht Jahre in meine Ausbildung investiert, damit 
ich dir hier Dinge beibringe, die du in jedem C-Buch, dass du bei Amazon 
um 12 Euro kaufen kannst, nachlesen kannst. Sonst kann ich dir das 
nämlich auch gleich fix fertig schreiben - das wäre für mich nämlich 
einfacher und geht auch schneller. Wer ein Projekt machen will, muss 
sich zuallererst fragen: was müsste ich dazu lernen, wo muss ich mich 
weiterbilden.
Leider scheint dieser Gedanke in der heutigen Zeit vielen Menschen immer 
mehr absurd vorzukommen, dass man Dinge die man machen will auch lernen 
muss.
Du willst eine Programmiersprache anwenden, um ein Problem zu lösen?
Dann lerne die Programmiersprache und wir können uns über deine 
Möglichkeiten zur Lösung des Problems unterhalten. Aber wenn ich dir 
erst mal jedes 3. Wort erklären muss, dann macht das einfach keinen 
Spass. Ich diskutiere nicht mit jemandem darüber, wie man einen 
englischsprachigen Roman aufbaut, der gerade mal 3 Brocken Englisch 
kann.

von Robert (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz ,
Ich habe auch mal dein  Ausführliches Beispiel mal ausprobiert.
An dieser stelle vielen Dank für die Erklährung.

Ich habe aber ein Problem, Das der Zeiger bei mir Verkehrt herum 
Angezeigt wird.

Zeiger hat den Wert Null (siehe Bild Zeiger_1 Wert null) und bei 100 
(siehe Bild Zeiger_1 Wert 100%).

Wo hab ich da jetz einen Denkfehler drin

mfg

von Karl H. (kbuchegg)


Lesenswert?

Robert schrieb:

> Wo hab ich da jetz einen Denkfehler drin

ohne Code ist das schwer zu sagen, aber ich denke, du hast nicht 
bedacht, dass mathematische Winkel ihren 0-Winkel auf der X-Achse haben 
und entgegegen dem Uhrzeigersinn größer werden.

von karl (Gast)


Lesenswert?

Hallo,
Ich habe mal das Beispiel von Karl-Heinz ausprobiert das Funktioniert 
1a.
So nun meine Frage, wie mus ich vorgehen das ich einen gefüllten Zeiger 
bekomme.

Da ich noch nicht so bewandert bin in C , gaher meine Frage.
Mfg

von Falk B. (falk)


Lesenswert?

Dann muss man sich eine Funktion erstellen, welche eine Fläche (Polygon) 
füllen kann und anstatt von Linien zeichnet man Polygone mit den 
gleichen Eckpunkten. Für den Anfang nimmt man das einfachste Polygon, 
das Dreieck. Dafür einen Füllalgorithmus zu schreiben ist mal ne gute 
Aufgabe.

Und wenn man das alles durch hat wird man feststellen, dass der Kram zu 
langsam ist. Dann nimmt man fertig gezeichnete Bilder, welche im Flash 
liegen, und kopiert sie einfach auf's LCD. Muss man nicht soviel rechnen 
und man kann beliebig komplexe Bilder/Animationen darstellen.

von karl (Gast)


Lesenswert?

Falk Brunner schrieb:

> Und wenn man das alles durch hat wird man feststellen, dass der Kram zu
> langsam ist. Dann nimmt man fertig gezeichnete Bilder, welche im Flash
> liegen, und kopiert sie einfach auf's LCD. Muss man nicht soviel rechnen
> und man kann beliebig komplexe Bilder/Animationen darstellen.


Ja aber dann bräuchte ich ja auch 180 Enzelbilder wenn die Teilung zb 
1Grad auflösung währ.

von karl (Gast)


Lesenswert?

Hallo,

So hier ist die Funktion um ein Dreick gefüllt zu Zeichnen.
Wie kann ich das nun mit  Karl Heinz Buchegger sein Beispiel verknüpfen,
muss ich denn für jeden Punkt am Dreieck diese Berechnung machen.
zb.


    tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
    tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;

fillTriangle(tmp[i].x,tmp[i].y,100,100,150,50, Red);

Könnte mir denn da jemand weiterhelfen.


1
void fillTriangle(u16 Ax,u16 Ay,u16 Bx,u16 By,u16 Cx,u16 Cy,u16 color)
2
{
3
  s16 dx1,dx2,dx3;
4
  u16 Sx,Ex,a,d,c;
5
  u8 Sy,Ey;
6
7
8
     if (Ay>By)
9
     {
10
      a = Ay;
11
      d = By;
12
      Ay = d; 
13
      By = a;
14
15
      a = Ax;
16
      d = Bx;
17
      Ax = d; 
18
      Bx = a;
19
     }
20
  
21
  
22
     if (Ay>Cy)
23
     {
24
      a = Ay;
25
      d = Cy;
26
      Ay = d; 
27
      Cy = a;
28
29
      a = Ax;
30
      d = Cx;
31
      Ax = d; 
32
      Cx = a;
33
     }
34
35
36
     if (By>Cy)
37
     {
38
      a = By;
39
      d = Cy;
40
      By = d; 
41
      Cy = a;
42
43
      a = Bx;
44
      d = Cx;
45
      Bx = d; 
46
      Cx = a;
47
     }
48
49
  if (By-Ay > 0) dx1=((Bx-Ax)<<8)/(By-Ay); else dx1=0;
50
  if (Cy-Ay > 0) dx2=((Cx-Ax)<<8)/(Cy-Ay); else dx2=0;
51
  if (Cy-By > 0) dx3=((Cx-Bx)<<8)/(Cy-By); else dx3=0;
52
53
  Ex=Sx=Ax<<8; Ey=Sy=Ay;
54
55
  if(dx1 > dx2) 
56
  {
57
    while(Sy<=By)
58
    {
59
      LCD_Draw_bresenham_line(Sx>>8,Sy++,Ex>>8,Ey++,color);
60
      Sx+=dx2;
61
      Ex+=dx1;
62
    }
63
    Ex=Bx<<8; Ey=By;
64
    while(Sy<=Cy)
65
    {    
66
      LCD_Draw_bresenham_line(Sx>>8,Sy++,Ex>>8,Ey++,color);
67
      Sx+=dx2;
68
      Ex+=dx3;
69
    }
70
  } 
71
  else 
72
  {
73
    while(Sy<=By)
74
    {      
75
      LCD_Draw_bresenham_line(Sx>>8,Sy++,Ex>>8,Ey++,color);
76
      Sx+=dx1;
77
      Ex+=dx2;
78
    }
79
    Sx=Bx<<8; Sy=By;
80
    while(Sy<=Cy)
81
    {    
82
      LCD_Draw_bresenham_line(Sx>>8,Sy++,Ex>>8,Ey++,color);
83
      Sx+=dx3;
84
      Ex+=dx2;
85
    }
86
  }
87
}

von karl (Gast)


Lesenswert?

Da kann mir wohl keiner Weiterhelfen .

von Karl H. (kbuchegg)


Lesenswert?

karl schrieb:
> Hallo,
>
> So hier ist die Funktion um ein Dreick gefüllt zu Zeichnen.
> Wie kann ich das nun mit  Karl Heinz Buchegger sein Beispiel verknüpfen,
> muss ich denn für jeden Punkt am Dreieck diese Berechnung machen.
> zb.
>
>
>     tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
>     tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;
>
> fillTriangle(tmp[i].x,tmp[i].y,100,100,150,50, Red);
>
> Könnte mir denn da jemand weiterhelfen.

Ja klar musst du alle Punkte deines Polygons entsprechend drehen.
Und danach ist es ein Flächenfüller, der das Polygon entsprechend 
gefüllt in die Pixel malt.

Zu deinem Dreicksfüller:
Es ist reichlich übertrieben, da jetzt einen Bresenham darauf 
anzusetzen, die einzelnen Linien für die Füllung hinzumalen.
Es gibt zwar mehrere Verfahren, wie man beliebige Polygone füllen kann, 
und das Tracen der Outline ist nicht so ohne, aber die Füllinien sind zb 
dergestalt, dass sie immer waagrecht, bzw. senkrecht verlaufen (je nach 
Verfahren). D.h. hier lohnt es sich schon wieder, eine Spezialroutine zu 
haben, die für diesen Fall (waagrechte bzw. senkrechte Linie) speziell 
optimiert ist, so dass man nicht auf den allgemeinen Fall zurückgreifen 
muss. Auch ist es je nach Polygon so, dass oftmals mehrere Pixel 
nebeneinander zu setzen sind, zum Beispiel 8, was bei einer LCD 
Aufteilung von 8 Pixel nebeneinander dazu führt, dass man nicht 
einzelpixelweise an die Sache ranngeht, sondern im Bildspeicher ein Byte 
komplett auf 'Schwarz' setzt, in dem Wissen, dass man dadurch 8 
nebeneinander liegende Pixel auf einmal gesetzt hat. Da steht dir jetzt 
Tür und Tor offen für eigene Ideen, was du noch alles optimieren bzw. 
vereinfachen kannst, weil du eben spezielle Fälle vorliegen hast, die du 
ausnutzen kannst.


Edit:
1
  Ey=Sy=Ay;
2
...
3
4
      LCD_Draw_bresenham_line(Sx>>8,Sy++,Ex>>8,Ey++,color);
5
...
Bei dir dann wohl waagrechte Linien. Denn Sy und Ey sind in deinem 
Scanline Verfahren immer gleich. Ob waagrecht jetzt so schlau ist oder 
nicht, kann hier keiner sagen, das hängt zum Beispiel auch von der 
Organisation deines Grafik-LCDs ab ob dort die BIts des Bildspeichers 
waagrechte oder senkrechte Pixel bevorzugen.

Aber sei es drum. Du hast jetzt einen Dreicksfüller basierend auf einem 
Scanline verfahren. Jetzt gilt es das auf allgemeinere Polygone zu 
verallgemeinern.

von karl (Gast)


Lesenswert?

Hallo,
Mein Versuch das Dreieck zu drehen geht  leider nicht so wie ich mir das 
dachte.

Vielleicht währe es möglich mir ein stück weiterhilft.
mfg


1
void Triangle_Rotate( u16 var )
2
{ 
3
 u16 x_center = 250;
4
 u16 y_center = 250;  
5
6
  float angleValue;
7
  float cosAlpha;
8
  float sinAlpha;
9
10
  angleValued = var ;
11
  
12
  angleValue = ( angleValued * 100.0 / 1023 ) * PI100;
13
  
14
 
15
  cosAlpha = cos( angleValue );
16
  sinAlpha = sin( angleValue );
17
 
18
19
  //A
20
    tmp[0].Ax = Zeiger[0].Ax * cosAlpha - Zeiger[0].Ay * sinAlpha;
21
    tmp[1].Ay = Zeiger[1].Ax * sinAlpha + Zeiger[1].Ay * cosAlpha;
22
  //B
23
    tmp[2].Bx = Zeiger[2].Bx * cosAlpha - Zeiger[2].By * sinAlpha;
24
    tmp[3].By = Zeiger[3].Bx * sinAlpha + Zeiger[3].By * cosAlpha;
25
  //C
26
    tmp[4].Cx = Zeiger[4].Cx * cosAlpha - Zeiger[4].Cy * sinAlpha;
27
    tmp[5].Cy = Zeiger[5].Cx * sinAlpha + Zeiger[5].Cy * cosAlpha;
28
29
30
/*  
31
//Das kann Glaube ich so nicht gehen
32
// X und Y-Center
33
  //A
34
    tmp[0].Ax += x_center;
35
    tmp[1].Ay += y_center;
36
  //B
37
    tmp[2].Bx += x_center;
38
    tmp[3].By += y_center;
39
  //C
40
    tmp[4].Cx += x_center;
41
    tmp[5].Cy += y_center;
42
*/  
43
44
    fillTriangle(tmp[0].Ax,
45
                 tmp[1].Ay,
46
                 tmp[2].Bx,
47
                 tmp[3].By,
48
                 tmp[4].Cx,
49
                 tmp[5].Cy, Red);
50
 }

von karl (Gast)


Lesenswert?

noch was vergessen

1
struct coord
2
{
3
  u16 Ax;
4
  u16 Ay;
5
  u16 Bx;
6
  u16 By;
7
  u16 Cx;
8
  u16 Cy;
9
};
10
11
12
13
14
struct coord Zeiger[] = 
15
{
16
   { 15,  15 },
17
   { 200, 210 },
18
   { 180, 70 }
19
};
20
21
struct coord tmp[6];

von karl (Gast)


Lesenswert?

Da kann mir scheinbar niemand weiterhelfen .
mfg

von Karl H. (kbuchegg)


Lesenswert?

Können schon.
Aber wo ist dann eigentlich deine Leistung?

Installier dir einen C-Compiler auf dem PC und teste und debugge deine 
Funktionen dort. Das geht dort wesentlich einfacher. Als Ausgabe machst 
du dir ein 2D-Array aus char, welches du danach (oder wenn du es 
brauchst auch zwischendurch) ausgibst. Ein '#' stellt ein gesetztes 
Pixel dar, ein Leerzeichen ein nicht gesetztes. Du kannst deine 
Funktionen mit printf spicken, so viel du willst und so viele wie du 
brauchst um nachzuvollziehen, was deine Funktion macht und mit dem zu 
vergleichen, wie sie eigentlich machen sollte.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Lesenswert?

karl schrieb:
> noch was vergessen
>
>
> struct coord
> {
>   u16 Ax;
>   u16 Ay;
>   u16 Bx;
>   u16 By;
>   u16 Cx;
>   u16 Cy;
> };
>

Eine 2D-Koordinate, also 1 Punkt, hat bei dir 6(!) Zahlenwerte?
Na bumm.

Da hast du aber so einiges überhaupt nicht verstanden.

von karl (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> karl schrieb:
>> noch was vergessen
>>
>>
>> struct coord
>> {
>>   u16 Ax;
>>   u16 Ay;
>>   u16 Bx;
>>   u16 By;
>>   u16 Cx;
>>   u16 Cy;
>> };
>>
>
> Eine 2D-Koordinate, also 1 Punkt, hat bei dir 6(!) Zahlenwerte?
> Na bumm.
>
> Da hast du aber so einiges überhaupt nicht verstanden.

Das wahr ja von mir auch völliger Quatsch.

ich bekomme das Dreieck mit den Coordinaten die im Struct sind nicht 
angezeigt.

Mit dem Ausgeklammerten Werten wirds Angezeigt.

1
struct coord
2
{
3
  u16 x;
4
  u16 y;
5
};
6
7
//TRIANGLE 
8
struct coord Zeiger[] = 
9
{
10
   { 100, 100 },
11
   { 150, 100 },
12
   { 150, 70 }
13
};
14
15
struct coord tmp[6];
16
17
void Triangle_Rotate( u16 var )
18
{ 
19
  int x1,x2,x3;
20
  int y1,y2,y3;
21
  int ax1,ax2,ax3,ay1,ay2,ay3;
22
  int refx,refy;
23
24
  
25
26
  float angleValue;
27
  float cosAlpha;
28
  float sinAlpha;
29
  float angleValued;
30
31
  
32
  angleValued=var ;
33
 
34
   
35
36
  angleValue = ( angleValued * 100.0 / 1023 ) * PI100;
37
  
38
39
  cosAlpha = cos( angleValue );
40
  sinAlpha = sin( angleValue );
41
42
43
//ROTATION TRIANGLE 
44
//Mittelpunkt  
45
  refx=100;
46
  refy=100;
47
48
/*
49
50
  x1=100;
51
  y1=100;
52
  x2=150;
53
  y2=100;
54
  x3=150;
55
  y3=70;
56
57
58
  ax1=refy+(x1-refx)*cosAlpha-(y1-refy)*sinAlpha;
59
  ay1=refy+(x1-refx)*sinAlpha+(y1-refy)*cosAlpha;
60
61
  ax2=refy+(x2-refx)*cosAlpha-(y2-refy)*sinAlpha;
62
  ay2=refy+(x2-refx)*sinAlpha+(y2-refy)*cosAlpha;
63
64
  ax3=refy+(x3-refx)*cosAlpha-(y3-refy)*sinAlpha;
65
  ay3=refy+(x3-refx)*sinAlpha+(y3-refy)*cosAlpha;
66
67
68
  fillTriangle(ax1,ay1,ax2,ay2,ax3,ay3, Yellow);  
69
 */
70
71
72
  tmp[0].x=refy+(Zeiger[0].x-refx)*cosAlpha-(Zeiger[0].y-refy)*sinAlpha;
73
  tmp[1].y=refy+(Zeiger[0].x-refx)*sinAlpha+(Zeiger[0].y-refy)*cosAlpha;
74
75
  tmp[2].x=refy+(Zeiger[1].x-refx)*cosAlpha-(Zeiger[1].y-refy)*sinAlpha;
76
  tmp[3].y=refy+(Zeiger[1].x-refx)*sinAlpha+(Zeiger[1].y-refy)*cosAlpha;
77
78
  tmp[4].x=refy+(Zeiger[2].x-refx)*cosAlpha-(Zeiger[2].y-refy)*sinAlpha;
79
  tmp[5].y=refy+(Zeiger[2].x-refx)*sinAlpha+(Zeiger[2].y-refy)*cosAlpha; 
80
  
81
82
  
83
   fillTriangle(tmp[0].x,tmp[1].y,tmp[2].x,tmp[3].y,tmp[4].x,tmp[5].y, Red);
84
85
86
}

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> karl schrieb:
>> noch was vergessen
>>
>>
>> struct coord
>> {
>>   u16 Ax;
>>   u16 Ay;
>>   u16 Bx;
>>   u16 By;
>>   u16 Cx;
>>   u16 Cy;
>> };
>>
>
> Eine 2D-Koordinate, also 1 Punkt, hat bei dir 6(!) Zahlenwerte?
> Na bumm.
>
> Da hast du aber so einiges überhaupt nicht verstanden.



Das erklärt auch den Quatsch
1
    tmp[0].Ax = Zeiger[0].Ax * cosAlpha - Zeiger[0].Ay * sinAlpha;
2
    tmp[1].Ay = Zeiger[1].Ax * sinAlpha + Zeiger[1].Ay * cosAlpha;

dabei hat das alles ja eigentlich schon ganz gut angefangen
1
  tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha;
2
  tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha;

du hast deinen Zeiger, der aus 3 Punkten besteht und die transformierst 
du
1
   for( i = 0; i < 3; i++ )
2
   {
3
     tmp[i].x = Zeiger[i].x * cosAlpha - Zeiger[i].y * sinAlpha + center.x;
4
     tmp[i].y = Zeiger[i].x * sinAlpha + Zeiger[i].y * cosAlpha + center.y;
5
   }

ehe dann die 3 transformierten Punkt in den Dreiecksfüller gesteckt 
werden. Ok, dem Füller könnte man eine bessere Schnittstelle verpassen, 
so dass sie zum Rest des 'Systems' passt(*), aber von abgekupfertem Code 
kann man eben nicht alles verlangen. Wenn der Füller erst mal 
funktioniert reicht das schon.
1
  fillTriangle( tmp[0].x, tmp[0].y,
2
                tmp[1].x, tmp[1].y,
3
                tmp[2].x, tmp[2].y );



(*) Wozu hat man sich denn sonst eine Struktur für 2D-Koordinaten 
eingeführt, wenn man dann erst recht wieder x und y getrennt in die 
Funktion reinschiebt. Ein Dreiecksfüller braucht 3 Punkte, die 3 
Eckpunkte des Dreiecks. 'Punkte', das ist aber jeweils nichts anderes 
als ein 'struct coord'

von karl (Gast)


Lesenswert?

so das geht,
aber wenn ich die Werte hier
struct coord center = { 100, 100 };
gösser oder kleiner mache dann kommt nur mist raus.

von karl (Gast)


Lesenswert?

Mir gings eigentlich darum , das gezeigte Beispiel von Karl-Heinz
den zeiger gefüllt Rotieren zulassen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.