Polygon auf LCD drehen

Gast #634123
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?
#634160
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.
Gast #634242
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 */
}
#634254
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.
Gast #634526
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);
  }
}
#634538
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren