Forum: Mikrocontroller und Digitale Elektronik Polygon auf LCD drehen


von Michael (Gast)


Lesenswert?

Hallo,

ich versuche ein Polygon auf dem LCD zu drehen.
Ich habe verschiedene Vektoren definiert die dann um einen bestimmten 
Winkel gedreht werden. Im Beispiel ist das ein Pfeil der aus mehreren 
Linien definiert ist.
Das Ganze dreht sich zwar, ergibt aber keinen gefüllten Pfeil da es 
irgendwo rundungsfehler gibt.
Das Display ist ein 128x64.

//Anfangs und Endkoordinaten der Linien, ergibt zusammengesetzt einen 
Kompasspfeil
s08 
px[]={4,4,3,3,2,2,1,1,0,0,-1,-1,-2,-2,-3,-3,-4,-4},py[]={6,8,2,7,-2,6,-7 
,5,-9,4,-7,5,-2,6,2,7,6,8};


rotate (px,py,30);//Aufruf zum drehen 30°

void rotate (s08* px,s08* py,u16 winkel)
{
 #define POINTS 18
 #define MITTEX 95
 #define MITTEY 31

 double arcus = (2  PI  winkel) / 360;
 float cosinus = cos( arcus );
 float sinus = sin( arcus );
 float x,y;

 for (u08 i = 0; i < POINTS; i++ )
 {
  x = px[i];
  y = py[i];
  px[i] = (cosinus  x - sinus  y);
  py[i] = (sinus * x + cosinus * y);
 }
 for (u08 i = 0; i < POINTS; i+=2 )
 DrawLine(px[i]+MITTEX,py[i]+MITTEY,px[i+1]+MITTEX,py[i+1]+MITTEY,BLACK);
}

kann mir jemand helfen den Fehler der Berechnung zu finden?

von Philipp Karbach (Gast)


Lesenswert?

nur eine annahme aber vielleicht bist du zu genau mit deiner rechnung, 
die line funktion arbeitet ja nur mit integern weil es keine halben 
pixel gibt, da wird natürlich gerundet.

von Philipp Karbach (Gast)


Lesenswert?

achja und defines am anfang einer funktion, das ist ziemlich hässlich, 
schreib sie lieber davor!

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:

> kann mir jemand helfen den Fehler der Berechnung zu finden?

Deine Rotations-Berechnung an und für sich ist korrekt.
Allerdings ist deine Annahme falsch, dass 2 pixelparallele
Linien nach einer Drehung immer noch pixelparallel sind.

Das Beste wäre es, wenn du dir eine Füllfunktion für deinen
Pfeil schreiben würdest, die nicht davon ausgeht, dass irgendwelche
Begrenzungslinien exakt senkrecht oder waagrecht sind. Das ist
zwar etwas Aufwand, wenn du allerdings deinen Pfeil in lauter
Dreiecke aufteilst und mit einer generellen Dreiecks-füll-funktion
arbeitest, sollte das machbar sein.

Eventuell könnte dich in der jetzigen Version noch eine Rundungs-
korrektur retten:

  px[i] = (int)((cosinus  x - sinus  y) + 0.5);
  py[i] = (int)((sinus * x + cosinus * y) + 0.5);

aber grosse Hoffnungen würde ich nicht darauf setzen.

von Michael (Gast)


Lesenswert?

Die Rundung mit +0,5 habe ich schon probiert, das hilft nur wenig.
Eine Füllfunktion für Dreiecke wäre sicherlich richtig, allerdings habe 
ich noch nichts (für mich) brauchbares im Netz gefunden.

ist diese Funktion geeignet?

#define BOARD WIDTH  10
#define BOARD HEIGHT 20

typedef struct MAP
{
  unsigned char b[BOARD HEIGHT][BOARD WIDTH];
} MAP;

static void flood_loop(MAP *map, int x, int y, unsigned int dst_c, 
unsigned int src_c)
{
  int fillL, fillR, i;
  int in_line = 1;

  /* find left side, filling along the way */
  fillL = fillR = x;
  while(in_line)
  {
    map->b[y][fillL] = dst_c;
    fillL--;
    in_line = (fillL < 0) ? 0 : (map->b[y][fillL] == src_c);
  }
  fillL++;

  /* find right side, filling along the way */
  in_line = 1;
  while(in_line)
  {
    map->b[y][fillR] = c;
    fillR++;
    in_line = (fillR > 9) ? 0 : (map->b[y][fillR] == fillC);
  }
  fillR--;

  /* search top and bottom */
  for(i = fillL; i <= fillR; i++)
  {
    if(y > 0 && map->b[y - 1][i] == fillC)
        flood_loop(map, i, y - 1, c, fillC);
    if(y < BOARD HEIGHT && map->b[y + 1][i] == fillC)
        flood_loop(map, i, y + 1, c, fillC);
  }
}

void flood_fill(MAP *map, int x, int y, unsigned int c)
{
  flood_loop(map, x, y, c, map->b[y][x]);
  map->b[y][x] = c;  /* some buggy optimizers needed this line */
}

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
> Die Rundung mit +0,5 habe ich schon probiert, das hilft nur wenig.

Dachte ich mir schon.

> Eine Füllfunktion für Dreiecke wäre sicherlich richtig, allerdings habe
> ich noch nichts (für mich) brauchbares im Netz gefunden.

zb hier
http://www.gamedev.net/community/forums/topic.asp?topic_id=256968

Eine generelle Füll Funktion für Dreicke ist nicht weiter
schwer. Das sollte eigentlich jeder, der Graphik Programmierung
macht, ohne gröbere Probleme hinbekommen.

>
> ist diese Funktion geeignet?

Das sieht mir mehr nach einem generellem Flood Fill aus.
Ist für deine Zwecke nicht unbedingt das Wahre, da du damit
eine allgemeine Füllfunktion hast, die du so (zumindest für
diesen Zweck) nicht brauchst.
Ausserdem musst du zuerst mit Linien deinen Pfeil aufmalen um ihn
nachher mit dem Flood Fill auszumalen.
Aussderdem gibt es Schwierigkeiten, wenn der Pfeil nicht für sich
alleine steht, sondern zb eine Linie unter dem Pfeil durchläuft.

von Michael (Gast)


Lesenswert?

das habe ich jetzt implementiert, hat aber auch noch einen Fehler den 
ich noch nicht gefunden habe:
//Dreieck auffüllen drei Eckpunkte werden übergeben

void trianglefiller (s08 ax,s08 ay,s08 bx,s08 by,s08 cx,s08 cy)
{
 s08 dx1,dx2,dx3,temp;

 //Eckpunkte sortieren mit kleinstem y-wert als a
 if(ay>=by){temp=by;by=ay;ay=temp;temp=bx;bx=ax;ax=temp;}
 if(ay>=cy){temp=cy;cy=ay;ay=temp;temp=cx;cx=ax;ax=temp;}
 if(by>=cy){temp=cy;cy=by;by=temp;temp=cx;cx=bx;bx=temp;}

 A.x=ax;
 A.y=ay;
 B.x=bx;
 B.y=by;
 C.x=cx;
 C.y=cy;

  if (B.y-A.y > 0) dx1=(B.x-A.x)/(B.y-A.y); else dx1=0;
  if (C.y-A.y > 0) dx2=(C.x-A.x)/(C.y-A.y); else dx2=0;
  if (C.y-B.y > 0) dx3=(C.x-B.x)/(C.y-B.y); else dx3=0;

  S=E=A;
  if(dx1 > dx2)
    {
    for(;S.y<=B.y;S.y++,E.y++,S.x+=dx2,E.x+=dx1)
      DrawLine(S.x,S.y,E.x,S.y,BLACK);
    E=B;
    for(;S.y<=C.y;S.y++,E.y++,S.x+=dx2,E.x+=dx3)
      DrawLine(S.x,S.y,E.x,S.y,BLACK);
  }
    else
    {
    for(;S.y<=B.y;S.y++,E.y++,S.x+=dx1,E.x+=dx2)
      DrawLine(S.x,S.y,E.x,S.y,BLACK);
    S=B;
    for(;S.y<=C.y;S.y++,E.y++,S.x+=dx3,E.x+=dx2)
      DrawLine(S.x,S.y,E.x,S.y,BLACK);
  }
}

von Karl H. (kbuchegg)


Lesenswert?

Michael wrote:
> das habe ich jetzt implementiert, hat aber auch noch einen Fehler den
> ich noch nicht gefunden habe:

(Ohne das jetzt ausprobiert zu haben)
Dein Problem wird wohl damit zu tun haben, dass dx1, dx2, dx3
float sein sollten und keine Intger Typen.
Falls du es noch nicht bemerkt hast, aber die dx stellen die
Steigungen der Dreieckskanten dar und die sind nun mal selten
ganzzahlig.

Bei deinen Zahlenwerten könnte man allerdings mal überprüfen
ob man anstelle von float nicht auch eine Fixpunktarithmetik
nehmen kann.

von Michael (Gast)


Lesenswert?

bis auf ein paar Punkte stimmt es jetzt.
Aber irgendwo gibt es noch punkte die daneben liegen.

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.