Forum: Mikrocontroller und Digitale Elektronik Warum kann man keine structs addieren?


von J. T. (chaoskind)


Lesenswert?

MoinMoin

ich versuche gerade mich ein wenig mit der Abbildung dreidimensionaler 
Körper auf dem 2dimensionalen Bildschirm zu befassen.

Hierzu habe ich mir mehrere structs erstellt.
1
typedef struct
2
{
3
    float X;
4
    float Y;
5
    float Z;
6
}sKoordinate;
7
8
typedef struct
9
{
10
    sKoordinate Punkt1;
11
    sKoordinate Punkt2;
12
    sKoordinate Punkt3;
13
}sDreieck;
14
15
sDreieck Dreieck1;
16
sDreieck TriSchieber;

dann setze ich die Koordinaten von Dreieck1 und TriSchieber noch auf 
sinnvolle Werte, und dann will ich das Dreieck1 um die Werte von 
TriSchieber verschieben, indem ich:
1
Dreieck += TriSchieber;
setze.

Dafür bekomme ich aber folgende Fehlermeldung:
\main.c|92|error: invalid operands to binary + (have 'sDreieck' and 
'sDreieck')|

Wieso kann ich denn die Structs nicht einfach addieren? Die sind doch 
beide vom gleichen Typ? Muss ich wirklich jedes Element einzeln 
addieren, oder gibt es da elegantere Lösungen?

Auch Googel hilft mir nicht so recht weiter, da ich nicht so recht weiß, 
was genau ich denn nun eigentlich fragen muss.... Die Fehlermeldung half 
nicht weiter, "Structs addieren" "Structs addieren in C" gab alles nur 
die Tendenz, dass ich die Elemente einzeln addieren muss...

Mit Dank im voraus und freundlichen Grüßen,

Chaos

P.S. Ich weiß, ein Dreieck ist noch kein dreidimensionaler Körper, aber 
ich wollts für die grundsätzliche "Verschiebungsübung" erstmal 
kleinhalten. Die Darstellung eines Kubus klappt schon, indem ich die 
Z-Koordinate jeweils mal sin45° nehme, und auf den jeweiligen X und 
Y-Wert addiere. Planare Projektion, oder so ähnlich, ich überflog da 
neulich mal den Wikiartikel, da wurden einige Arten der Projektion 
genannt, und da will ich mich nun langsam rantasten.

: Verschoben durch User
von lochness (Gast)


Lesenswert?

Das ist eigentlich eine klassische Anwendung einer objektorientierten 
Sprache wie C++.

Da geht das dann.

Du überlädst den "+" Operator, indem Du dann die Elemente einzeln 
addierst.

von J. T. (chaoskind)


Lesenswert?

P.P.S.

Ich arbeite mit CodeBlocks13.12 mit dem GCC wenn ich mich nicht irre. 
Und das ganze aufm PC/Schlepptop.

Soo schnell hät ich mit keiner Antwort gerechnet.
Ja dass sich das ganze sehr OO anfühlt, ist mir auch schon 
aufgefallen....

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

J. T. schrieb:
> Wieso kann ich denn die Structs nicht einfach addieren? Die sind doch
> beide vom gleichen Typ? Muss ich wirklich jedes Element einzeln
> addieren, oder gibt es da elegantere Lösungen?
C ist eine auf Minimalismus ausgelegte Sprache. Die kann solche Gimmicks 
nicht. Auch Funktionsüberladungen sind den C-Usern ja schon zu 
abgefahren.

Verwende einfach eine Sprache, die das Definieren eigener Operatoren 
erlaubt, wie z.B. C++:
1
struct sKoordinate
2
{
3
    float X;
4
    float Y;
5
    float Z;
6
};
7
8
struct sDreieck
9
{
10
    sKoordinate Punkt1;
11
    sKoordinate Punkt2;
12
    sKoordinate Punkt3;
13
};
14
15
sKoordinate operator + (const sKoordinate& a, const sKoordinate& b) {
16
  return { a.X + b.X, a.Y + b.Y, a.Z + b.Z };
17
}
18
19
sDreieck operator + (const sDreieck& a, const sDreieck& b) {
20
  return { a.Punkt1 + b.Punkt1, a.Punkt2 + b.Punkt2, a.Punkt3 + b.Punkt3 };
21
}
22
23
int main () {
24
  sDreieck d1, d2, d3;
25
  d3 = d1 + d2;
26
}

C++ erlaubt noch eine Menge weitere nützliche Dinge, um Programme kürzer 
und einfacher zu machen. Aber gleich kommen wieder die Leute, die 
erklären dass C++ viel zu viel kann, dich dazu zwingt das böse OOP zu 
nutzen (falsch), nicht für jede noch so obskure Plattform verfügbar ist, 
usw.

von J. T. (chaoskind)


Lesenswert?

Programmierer schrieb:
> int main () {
>   sDreieck d1, d2, d3;
>   d3 = d1 + d2;
> }

und die Uunterscheidung welchen der "Plusoperatoren" er nutzen soll, 
trifft er dann anhand des Typs?
Sprich wenn ich nebenbei noch 2 int habe, i und j meinetwegen, dann 
kommt er da nicht durcheinander, wenn ich sowas wie:

d3 = d1 + d2;
i = j + 4;

schreibe?

Kennst du zufällig Allegro? Das nutze ich für die grafische Ausgabe.

Programmierer schrieb:
> Aber gleich kommen wieder die Leute, die
> erklären dass C++ viel zu viel kann, dich dazu zwingt das böse OOP zu
> nutzen (falsch)

Also wenn ich mit OOP so einfach sowas machen kann, kann es sooo böse 
nicht sein ;-)

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

J. T. schrieb:
> und die Uunterscheidung welchen der "Plusoperatoren" er nutzen soll,
> trifft er dann anhand des Typs?
Ja.

J. T. schrieb:
> Sprich wenn ich nebenbei noch 2 int habe, i und j meinetwegen, dann
> kommt er da nicht durcheinander, wenn ich sowas wie
Im Zweifelsfall gibts nen "ambiguous overload" error oder so.

J. T. schrieb:
> Kennst du zufällig Allegro? Das nutze ich für die grafische Ausgabe.
nur vom Hören.

J. T. schrieb:
> Also wenn ich mit OOP so einfach sowas machen kann, kann es sooo böse
> nicht sein ;-)
Tja. der code ist jetzt noch nicht sehr objektorientiert, nutzt nur die 
Features der Sprache, die für OOP gedacht sind.

von Karl H. (kbuchegg)


Lesenswert?

> und dann will ich das Dreieck1 um die Werte von TriSchieber verschieben

Abgesehen davon macht allerdings auch die ganze Operation keinen Sinn. 
Denn was soll das sein, ein Dreieck um ein anderes Dreieck zu 
verschieben? Ein Dreieck, allgmeiner irgendeine Form, wird um einen 
Vektor verschoben. Eine Verschiebeoperation ist dann wiederrum weiter 
eine Spezialisierung einer Matrixoperation. D.h. die ganz allgemeine 
Form sieht so aus:
Du beschreibst Objekte indem du ihre Eckpunktskoordinaten als Vektoren 
darstellst.
Du manipulierst Objekte (verschieben, drehen, skalieren) indem du eine 
entsprechende Matrix Operation auf jeden Eckpunkt anwendest.
Die grundlegende Operation dazu ist also die Multiplikation eines 
Vektors mit einer Matrix welche als Ergebnis den so manipulierten Vektor 
hat. Ein Objekt, gegeben durch seine Eckpunktsvektoren wird dann 
mithilfe der Matrix als ganzes manipuliert, indem man jeden Eckpunt mit 
der Matrix multipliziert.
Aber es schadet auch nichts, erst mal Verschiebungen als eigene 
Operation zu definieren, die einen Vektor und ein Objekt nimmt, und den 
Verschiebevektor auf das Objekt anwendet. Dasselbe dann für Rotation. 
Irgendwann merkt man dann schon, warum es sich lohnt, wenn man beide 
Manipulationen mit ein und derselben Operation ausdrücken kann. Von da 
weg, ist es dann auch bis zur perspektivischen Abbildung nicht mehr 
weit.

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

Karl H. schrieb:
> Abgesehen davon macht allerdings auch die ganze Operation keinen Sinn.
> Denn was soll das sein, ein Dreieck um ein anderes Dreieck zu
> verschieben? Ein Dreieck, allgmeiner irgendeine Form, wird um einen
> Vektor verschoben. Eine Verschiebeoperation ist dann wiederrum weiter
> eine Spezialisierung einer Matrixoperation. D.h. die ganz allgemeine

Ja genau so mach ich das auch. Das "Verschiebedreick" ist kein Dreieck, 
sondern nur ein Platzhalter, da steht an jedem Punkt einfach nur der 
Wert, um den Verschoben wird. Und während ich das hier schreibe, merke 
ich, dass das tatsächlich unnötig ist. Ich brauche ja lediglich eine 
einzelne Koordinate (ein Vektor ist es dann wohl, bzw Skalar?) um die 
ich Verschiebe.

Mit dem Dreieck hatte ich mir das gedacht, das ich einfach jeden Punkt 
des Verschiebedreiecks auf den selben Wert setze, und dann die beiden 
"Dreiecke" einfach addiere. Aber da ich Structs nicht direkt addieren 
kann, langt dann wohl der einzelne Verschiebewert.

Karl H. schrieb:
> Du beschreibst Objekte indem du ihre Eckpunktskoordinaten als Vektoren
> darstellst.

Genau das hab ich ja vor, deshalb hatte ich den Verschiebewert/Vektor 
quasi als Dreieck aufgefasst, damit die Structs gleich sind. Ich dachte 
halt, wenn man die Structs gleich hält, könnte man sie addieren, das 
hätte das alles vereinfacht.

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:

>  Und während ich das hier schreibe, merke ich, dass das tatsächlich
> unnötig ist. Ich brauche ja lediglich eine einzelne Koordinate
> (ein Vektor ist es dann wohl, bzw Skalar?) um die ich Verschiebe.

ein Vektor

> Ich dachte
> halt, wenn man die Structs gleich hält, könnte man sie addieren, das
> hätte das alles vereinfacht.

Äh. Nicht wirklich.
Momentan ist deine Basisoperation die Addition 2-er Vektoren. Um ein 
Dreieck um einen Vektor zu verschieben, verschiebst du jeden einzelnen 
der 3 Punkte des Dreiecks um denselben Vektor.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Programmierer schrieb:
> Aber gleich kommen wieder die Leute, die erklären dass C++ viel zu
> viel kann, dich dazu zwingt das böse OOP zu nutzen (falsch), nicht für
> jede noch so obskure Plattform verfügbar ist, usw.

Dabei nutzt dein Beispiel nicht einmal OOP, sondern lediglich das
Überladen von Operatoren.

Doch das ist aber gerade einer der Voreile von C++ gegenüber anderen
OOP-Sprachen: Man kann OOP gewinnbringend dort nutzen, wo sie sinnvoll
erscheint, für alle anderen Fälle wird auch prozedurale Programmierung
unterstützt.

J. T. schrieb:
> Genau das hab ich ja vor, deshalb hatte ich den Verschiebewert/Vektor
> quasi als Dreieck aufgefasst, damit die Structs gleich sind.

Beim Überladen von Operatoren dürfen die beiden Operanden auch
unterschiedlichen Typs sein. Es stellt sich allenfalls die Frage, ob "+"
das richtige Symbol für das Verschieben eines geometrischen Objekts
entlang eines Vektors ist, oder ob man diese Funktion bzw. Methode nicht
besser "translate" o.ä. nennt.

von J. T. (chaoskind)


Lesenswert?

Karl H. schrieb:
> Momentan ist deine Basisoperation die Addition 2-er Vektoren. Um ein
> Dreieck um einen Vektor zu verschieben, verschiebst du jeden einzelnen
> der 3 Punkte des Dreiecks um denselben Vektor.

Und genau das tue ich doch. Bzw dachte, es würde so funktionieren.

Verschiebedreieck.Punkt1(1,0,0);  (x,y,z,)
Verschiebedreieck.Punkt2(1,0,0);
Verschiebedreieck.Punkt3(1,0,0);

Im Verschiebedreieck jeden Punkt auf den Verschiebvektor setzen.

Dreieck.Punkt1(0,0,0);
Dreieck.Punkt2(6,0,0);
Dreieck.Punkt3(3,3,0);

Das Dreieck erzeugen. Beide "Dreiecke" addieren, würde jeden 
Vektor/Eckpunkt des Dreiecks um einen auf der X-Achse verschieben, ginge 
es denn so, wie ich mir das gedacht hätte.

Aber dann schreib ich die Funktion erstmal so, das sie halt die Vektoren 
einzeln addiert. Oder ich schau mir doch mal C++ an....

Schaunmermal. Ich versuch erstmal weiter.

Vielen Dank für eure Antworten, evtl meld ich nochmal hierzu.

MfG Chaos

von J. T. (chaoskind)


Lesenswert?

Yalu X. schrieb:
> Beim Überladen von Operatoren dürfen die beiden Operanden auch
> unterschiedlichen Typs sein.

Was genau ist dieses Überladen nun eigentlich? Ist dass das definieren 
von Operatoren, wie "Programmierer" das gezeigt hat? Und gehört das nun 
zu C++ oder zu C?

Yalu X. schrieb:
> oder ob man diese Funktion bzw. Methode nicht
> besser "translate" o.ä. nennt.

Ja ich glaube, das ist ein guter Name.

von Markus F. (mfro)


Lesenswert?

Natürlich ist das sozusagen eine "Hochglanzaufgabe" für eine 
objektorientierte Sprache wie C++.

Nichtsdestotrotz läßt sie sich auch mit einer nicht objektorientierten 
Sprache lösen. Auch schon zu Fortran-Zeiten wurden erfolgreich Punkte in 
den Raum projiziert (tatsächlich haben auch heutzutage viele 
CAD-Programme einen nicht objektorientierten Fortran-Kern).

Und - ehrlich gesagt - C++ und Vektorrechnung bei Adam und Eva 
angefangen gleichzeitig zu lernen, scheint mir doch ein wenig heftig.

Alles, was in C kein "basic type" (char, short, int, long, float und 
double) kann man nicht mit den Standard-Operatoren addieren. Dafür 
gibt's Funktionen. Der angehängte Code verschiebt das Dreieck t um den 
Vektor v:
1
struct vector
2
{
3
        float x;
4
        float y;
5
        float z;
6
};
7
8
9
struct triangle
10
{
11
        struct vector a;
12
        struct vector b;
13
        struct vector c;
14
};
15
16
struct vector add_vector(struct vector v, struct vector w)
17
{
18
        struct vector rv;
19
20
        rv.x = v.x + w.x;
21
        rv.y = v.y + w.y;
22
        rv.z = v.z + w.z;
23
24
        return rv;
25
}
26
27
struct triangle move_triangle(struct triangle t, struct vector v)
28
{
29
        struct triangle rt;
30
31
        rt.a = add_vector(t.a, v);
32
        rt.b = add_vector(t.b, v);
33
        rt.c = add_vector(t.c, v);
34
35
        return rt;
36
}
37
38
int main(int argc, char *argv[])
39
{
40
        struct vector v = { 0.0, 1.0, 0.0 };
41
        struct triangle t =
42
        {
43
                { 0.0, 0.0, 0.0 },
44
                { 1.0, 0.0, 0.0 },
45
                { 1.0, 1.0, 0.0 },
46
        };
47
48
        struct triangle r = move_triangle(t, v);
49
}
~

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:

> Und genau das tue ich doch. Bzw dachte, es würde so funktionieren.
>
> Verschiebedreieck.Punkt1(1,0,0);  (x,y,z,)
> Verschiebedreieck.Punkt2(1,0,0);
> Verschiebedreieck.Punkt3(1,0,0);

Man kann natürlich auch von hinten durch die Brust ins Auge arbeiten.

Aber frag dich mal selber. So ein Verschiebedreick ist ja eigentlich 
auch nur ein Dreieck. Erfüllt dein Verschiebedreieck die Anforderung an 
ein Dreieck? Damit man bei einer Ansammlung von 3 Punkten von einem 
Dreieck sprechen kann, müssen ja ein paar Bedingungen erfüllt sein. Zum 
Beispiel die, das keine 2 Punkte gleich sind - denn dann ist es ja kein 
Dreieck mehr, sondern eine Linie.

Wie machst du das, wenn du ein Objekt mit 128 Eckpunkten verschieben 
willst? Wie mit einem Kreis?

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

J. T. schrieb:
> Was genau ist dieses Überladen nun eigentlich? Ist dass das definieren
> von Operatoren, wie "Programmierer" das gezeigt hat? Und gehört das nun
> zu C++ oder zu C?
operatoren überladen gibts nur in C++. Man kann in C++ außerdem 
Funktionen überladen, z.B.:
1
void print (int value) {
2
  // einen Integer ausgeben
3
}
4
5
void print (std::string value) {
6
  // Einen String ausgeben
7
}
8
9
int main () {
10
  print (42);
11
  print ("The answer\n");
12
}

Der C++ Compiler wählt hier jeweils automatisch die richtige Funktion 
aus, je nach Typ der Argumente. Das können viele statisch typisierte 
Sprachen, nur C mal wieder nicht. Der enorme Nutzen davon macht sich 
aber erst bei komplexeren Programmen bemerkbar; wenn man Variablen 
ausgibt, deren Typ man vielleicht später noch ändert, wird immer 
automatisch die richtige Ausgabetechnik verwendet. In C muss man bei 
printf fix im Formatstring angeben was für einen Typ die Variable hat, 
und wenn man den später ändert muss man alle printf's suchen, die die 
Variable ausgeben, und abändern.

von J. T. (chaoskind)


Lesenswert?

Wow da kam ja noch einiges =).

Markus F. schrieb:
> Der angehängte Code verschiebt das Dreieck t um den
> Vektor v:

Ich glaube, da hab ich in der Zwischenzeit etwas sehr ähnliches 
ausgetäuftelt. Vermutlich wieder von hinten durchs Auge in die Brust 
*gg. Aber trotzdem danke ich dir recht herzlich für den Code =)
1
int main()
2
{
3
    int32_t i = 0;
4
    sKoordinate Schiebevektor = {0.005, 0, 0};
5
    sDreieck Dreieck1 = { {-1.0, -0.125, 0}, {-0.75, -0.125, 0}, {-0.875, 0.125, 0} };
6
7
    allegro_init();
8
    install_keyboard();
9
    install_mouse();
10
    set_color_depth(16);
11
    set_gfx_mode( GFX_AUTODETECT_WINDOWED, X_Aufloesung, Y_Aufloesung, 0, 0);
12
13
    Bildpuffer1 = create_bitmap(X_Aufloesung, Y_Aufloesung);
14
    Bildpuffer2 = create_bitmap(X_Aufloesung, Y_Aufloesung);
15
16
17
    for(i=0; i<400; i++)
18
    {
19
        Bildpuffer1 = DreieckZeichnen(Dreieck1);
20
        draw_sprite(screen, Bildpuffer1, 0, 0);
21
        Dreieck1 = DreiecksVerschiebung(Schiebevektor, Dreieck1);
22
    }
23
24
25
    return 0;
26
}
27
END_OF_MAIN();
28
29
BITMAP* DreieckZeichnen(sDreieck Tri)
30
{
31
    BITMAP *bmp;
32
    int32_t X_Halter1 = 0;
33
    int32_t X_Halter2 = 0;
34
    int32_t Y_Halter1 = 0;
35
    int32_t Y_Halter2 = 0;
36
37
    bmp = create_bitmap(X_Aufloesung, Y_Aufloesung);
38
39
    X_Halter1 = (Tri.Punkt1.X * X_Aufloesung/2) + X_Aufloesung/2;
40
    X_Halter2 = (Tri.Punkt2.X * X_Aufloesung/2) + X_Aufloesung/2;
41
    Y_Halter1 = (Tri.Punkt1.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
42
    Y_Halter2 = (Tri.Punkt2.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
43
    line(bmp, X_Halter1, Y_Halter1, X_Halter2, Y_Halter2, makecol(255, 0, 0));
44
45
    X_Halter1 = (Tri.Punkt2.X * X_Aufloesung/2) + X_Aufloesung/2;
46
    X_Halter2 = (Tri.Punkt3.X * X_Aufloesung/2) + X_Aufloesung/2;
47
    Y_Halter1 = (Tri.Punkt2.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
48
    Y_Halter2 = (Tri.Punkt3.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
49
    line(bmp, X_Halter1, Y_Halter1, X_Halter2, Y_Halter2, makecol(0, 255, 0));
50
51
    X_Halter1 = (Tri.Punkt3.X * X_Aufloesung/2) + X_Aufloesung/2;
52
    X_Halter2 = (Tri.Punkt1.X * X_Aufloesung/2) + X_Aufloesung/2;
53
    Y_Halter1 = (Tri.Punkt3.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
54
    Y_Halter2 = (Tri.Punkt1.Y * Y_Aufloesung/2) + Y_Aufloesung/2;
55
    line(bmp, X_Halter1, Y_Halter1, X_Halter2, Y_Halter2, makecol(0, 0, 255));
56
57
    return bmp;
58
}
59
60
sKoordinate Translation(sKoordinate Schiebevektor, sKoordinate Punkt)
61
{
62
    Punkt.X += Schiebevektor.X;
63
    Punkt.Y += Schiebevektor.Y;
64
    Punkt.Z += Schiebevektor.Z;
65
66
    return Punkt;
67
}
68
69
70
sDreieck DreiecksVerschiebung(sKoordinate Schiebevektor, sDreieck Dreieck)
71
{
72
    Dreieck.Punkt1 = Translation(Schiebevektor, Dreieck.Punkt1);
73
    Dreieck.Punkt2 = Translation(Schiebevektor, Dreieck.Punkt2);
74
    Dreieck.Punkt3 = Translation(Schiebevektor, Dreieck.Punkt3);
75
76
    return Dreieck;
77
}

Karl H. schrieb:
> Aber frag dich mal selber. So ein Verschiebedreick ist ja eigentlich
> auch nur ein Dreieck. Erfüllt dein Verschiebedreieck die Anforderung an
> ein Dreieck? Damit man bei einer Ansammlung von 3 Punkten von einem
> Dreieck sprechen kann, müssen ja ein paar Bedingungen erfüllt sein. Zum
> Beispiel die, das keine 2 Punkte gleich sind - denn dann ist es ja kein
> Dreieck mehr, sondern eine Linie.

Ja das ist mir soweit klar. Warum ich das so versucht hab ich ja 
erklärt. Nachdem das nun nicht so geht, bin ich auch dazu übergegangen 
nur noch einen einzelnen Verschiebevektor zu definieren. Ich hing halt 
einfach an dem Gedanken fest, das man gleich Structs doch eigentlich 
"einfach so" addieren können müsste. Auch an dich vielen Dank für deine 
wie immer hilfreichen Worte =)

Programmierer schrieb:
> operatoren überladen gibts nur in C++. Man kann in C++ außerdem
> Funktionen überladen, z.B.:

Ich glaube, das hab ich im groben jetzt verstanden, auch dir danke ich 
recht herzlich für die Hilfe =)

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:

> Ich glaube, da hab ich in der Zwischenzeit etwas sehr ähnliches
> ausgetäuftelt.

Na das sieht doch schon gar nicht mal so schlecht aus.

Eines noch.
Hier zum Beispiel
1
sKoordinate Translation(sKoordinate Schiebevektor, sKoordinate Punkt)
2
{
3
....

jedes mal, wenn diese Funktion aufgerufen wird, wird eine komplette 
Kopie der Argumente erzeugt. Das kostet.
1
     Punkt.X += Schiebevektor.X;
2
     Punkt.Y += Schiebevektor.Y;
3
     Punkt.Z += Schiebevektor.Z;
beim Punkt ist es ok, wenn du eine Kopier erzeugen lässt, denn diese 
Kopier kannst du sinnvoll nutzen, weil du sie ja in der Funktion lokal 
verändern willst.
Aber der Schiebevektor, der braucht keine Kopie zu sein. Wenn dir der 
Aufrufer ganz einfach sein Objekt zur Verfügung stellt und du damit 
arbeiten kannst, dann wäre das völlig ausreichend.
dazu reicht es aber, wenn dir der Aufrufer einen Pointer von seinem 
Objekt zur Verfügung stellt.
1
sKoordinate Translation(sKoordinate* Schiebevektor, sKoordinate Punkt)
2
{
3
...

und da du nett bist, gibst du dem Aufrufer auch die Zusicherung: mach 
dir keine Sorgen, ich werde den Pointer nicht benutzen um damit dein 
Obekt zu verändern. Ich werde den Pointer nur benutzen um damit die 
Werte innerhalb des Objektes abzufragen, aber abgesehen davon vergreife 
ich mich nicht daran
1
sKoordinate Translation( const sKoordinate* Schiebevektor, sKoordinate Punkt )
2
{
3
  ...

im ganzen Satz
1
sKoordinate Translation( const sKoordinate* Schiebevektor, sKoordinate Punkt )
2
{
3
    Punkt.X += Schiebevektor->X;
4
    Punkt.Y += Schiebevektor->Y;
5
    Punkt.Z += Schiebevektor->Z;
6
7
    return Punkt;
8
}

Dasselbe hier
1
sDreieck DreiecksVerschiebung(sKoordinate Schiebevektor, sDreieck Dreieck)
hier wird nicht nur eine Koordinate kopiert, sondern es wird auch eine 
Kopie des kompletten Dreiecks angelegt. Und das bei jedem Aufruf!
Auch hier
1
BITMAP* DreieckZeichnen(sDreieck Tri)

gut. in der jetzigen Phase ist das wahrscheinlich noch kein Thema. Aber 
wenn das einmal mehr wird, dann machen sich diese unnötigen Kopien 
bemerkbar. D.h. du solltest jetzt dein Parameter Passing Wissen auf 
Vordermann bringen. In C muss man eben ziemlich viele Dinge einwandfrei 
beherrschen, um vernünftig programmieren zu können.

Zumal. Irgendwann werden dir Dreiecke nicht mehr genügen. Irgendwann 
bist du bei Vielecken mit zb 256 Eckpunkten, die zb einen Kreis 
darstellen sollen. Soll das jedesmal, bei jedem Funktionsaufruf immer 
wieder kopiert werden?

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

Karl H. schrieb:
> gut. in der jetzigen Phase ist das wahrscheinlich noch kein Thema. Aber
> wenn das einmal mehr wird, dann machen sich diese unnötigen Kopien
> bemerkbar. D.h. du solltest jetzt dein Parameter Passing Wissen auf
> Vordermann bringen. In C muss man eben ziemlich viele Dinge einwandfrei
> beherrschen, um vernünftig programmieren zu können.

Ich seh den Punkt auf den du hinauswillst! Das ist ein guter Einwand, 
das werd ich mir direkt mal zur Brust nehmen.

Karl H. schrieb:
> Zumal. Irgendwann werden dir Dreiecke nicht mehr genügen. Irgendwann
> bist du bei Vielecken mit zb 256 Eckpunkten, die zb einen Kreis
> darstellen sollen. Soll das jedesmal, bei jedem Funktionsaufruf immer
> wieder kopiert werden?

Es sollen irgendwann ja nicht mehr bloße flächenbehaftete "Körper" 
dargestellt werden, sondern volumenbehaftete Körper. Diese habe ich vor, 
aus Dreiecken zusammenzusetzen. Mir schwirrt da aus grauer Vorzeit noch 
was von Polygonen im Kopf rum ;-). Das wird der nächste Schritt. Und 
danach muss ich mir etwas ausdenken, das berechnen kann, welche Punkte 
dann inerhalb einer solchen Figur sind, damit sich dann an solche Sachen 
wie kollisionserkennung machen kann. Ist zumindest der langfristige 
Plan. Mal sehen, auf welche Hindernisse ich da so stoße.

Es macht immer wieder Spass von dir zu lernen, du bringst die Sachen auf 
den Punkt und mir fällt es meist leicht, deinen Ausführungen zu folgen. 
Find ich wirklich spitze!

von J. T. (chaoskind)


Lesenswert?

Karl H. schrieb:
> Punkt.Z += Schiebevektor->Z;

Vor einiger Zeit hatte ich schonmal die Frage nach dem Unterschied 
zwischen Bla.Sülz und Bla->Sülz gefragt. Da hattest du auch schon was 
gesagt, jetzt ist mir das grad völlig klar geworden ;-)!

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:

> Es sollen irgendwann ja nicht mehr bloße flächenbehaftete "Körper"
> dargestellt werden, sondern volumenbehaftete Körper. Diese habe ich vor,
> aus Dreiecken zusammenzusetzen.

Auch das kann man machen. Hängt davon ab, wie 'komplex' dann letzten 
Endes die Seitenflächen werden können.
Einfache konvexe Polygone kann man auch 'on the fly' in Dreiecke 
zerlegen. Das hat wiederrum den Vorteil, dass man nicht jeden Punkt 
x-mal speichern muss. Für beliebige Polygone ist das Verfahren etwas 
komplizierter, ist aber immer noch relativ simpel handhabbar, solange 
keine Löcher in den Polygonen vorkommen. Mit Löchern wirds dann noch 
einmal einen Tick komplexer.
Aber da hast du noch einiges vor dir.

> Mir schwirrt da aus grauer Vorzeit noch
> was von Polygonen im Kopf rum ;-). Das wird der nächste Schritt. Und
> danach muss ich mir etwas ausdenken, das berechnen kann, welche Punkte
> dann inerhalb einer solchen Figur sind, damit sich dann an solche Sachen
> wie kollisionserkennung machen kann.

Och das ist an und für sich gar nicht so schwer. So ein Dreieck (oder 
allgemeiner ein Polygon) hat ja 2 unterscheidbare Seiten. Mach dich dann 
mal zum Thema 'Normalvektor' schlau. Der hilft dir die Seiten zu 
unterscheiden. In einem konvexen Körper ist ein Punkt genau dann innen, 
wenn er von allen Seitenflächen aus betrachtet auf der 'Hinterseite' 
dieser Fläche liegt. Im allgemeinen Fall eines beliebigen Körpers wirds 
etwas kniffliger. Allerdings macht man bei derartigen 
Kollisionserkennungen sowieso einen Vortest mit einem oder mehreren 
einfachen geometrischen Körper(n). Anstatt die 2 Millionen Dreiecke zu 
untersuchen, die die Rosen mitsamt Blumenvase beschreiben, stellt man 
das Objekt gedanklich erst mal in einen entsprechend grossen Quader. 
Liegt der zu untersuchende Punkt nicht in diesem Quader, dann braucht 
man auch die 2 Millionen Dreiecke der Rosen nicht weiter untersuchen.

> Plan. Mal sehen, auf welche Hindernisse ich da so stoße.

Viel Spass. Geometrie und Computergraphik ist ein spannendes und 
vielfältiges Thema.

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Karl H. schrieb:

> Einfache konvexe Polygone kann man auch 'on the fly' in Dreiecke
> zerlegen.

Das geht dann zum Beispiel so (Bild)

Einen Punkt des Polygons hält man fest. Das ist quasi der eine Punkt, 
der in allen Dreiecken gemeinsam vorkommt. Die restlichen beiden Punkte 
der Dreiecke erhält man, indem man jeweils 2 aufeinanderfolgende Punkte 
des Polygons hernimmt. Zusammen mit dem fixen Punkt hat man dann ein 
Dreieck, welches durch die Rendering Pipeline gejagt wird.

Vorteil: Um dieses Polygon zu speichern, braucht man sich nur die 6 
Eckpunkte des Polygons merken (und transformieren). Aus lauter Dreiecken 
zusammen gesetzt wären das 4 Dreiecke und da jedes Dreick, no na, aus 3 
Punkten besteht, 12 zu merkende Punkte. (die natürlich auch 
transformiert werden müssten). D.h. man hat 6 
Koordinatentransformationen eingespart und sich im Gegenzug einen 
relativ einfachen Algorithmus (für konvexe Polygone!) zum zeichnen 
eingefangen
1
void DrawPolygon( const struct Polygon* poly )
2
{
3
  struct triangle renderTri;
4
5
  renderTri.Punkt1 = poly->coordinates[0];
6
7
  for( i = 1; i < poly->nrCoordinates - 1; i++ )
8
  {
9
    renderTri.Punkt2 = poly->coordinate[i];
10
    renderTri.Punkt3 = poly->coordinate[i+1];
11
12
    DrawTriangle( &renderTri );
13
  }
14
}

: Bearbeitet durch User
von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

HILFE!!,
was ist denn jetzt passiert?

Ich wollte das bei der Translation testweise auf den Pointer ändern, da 
hat er mir nen Fehler geworfen. Zeile 11...

int main()
{  <----Zeile 11.

Davor würde er ein "=", ";" usw erwarten....?!?!

Ich hab die Änderungen wieder rückgängig gemacht, aber immernoch die 
Fehlermeldung.

Könntest du die beiden Dateien evtl mal überfliegen, ob dir irgendwas 
offensichtliches ins Auge springt, was ich übersehen hab?

von Karl H. (kbuchegg)


Lesenswert?

Du hast hier im Header File
1
    sKoordinate Punkt8;
2
}sKubus;
3
4
5
sKoordinate Translation(sKoordinate Schiebevektor, sKoordinate Punkt);
6
sDreieck DreiecksVerschiebung(sKoordinate Schiebevektor, sDreieck Dreieck)   <-----

irrtümlich den ; gelöscht.

von J. T. (chaoskind)


Lesenswert?

Ich danke dir.

Ich habs in meiner Verzweiflung nun in nem neuen Projekt nochmal 1:1 
zusammenkopiert, hab das Apostroph dort aber eingefügt, vermutlich 
dachte ich ich habs nicht mit kopiert.... Das hat wieder funktioniert, 
da war die Verwunderung groß. Aber das fehlende ; erklärts natürlich.

Ich hasse das Apostroph, man übersieht ein fehlendes so leicht... Obwohl 
das immer das erste ist wonach ich schaue *gg.

von Karl H. (kbuchegg)


Lesenswert?

:-)

Wenn du seltsame unerklärliche Fehlermeldungen bekommst, dann immer von 
der Stelle des Auftretens des Fehlers rückwärts suchen ob in einer(!) 
der vorhergehenden Zeilen ein ; vergessen wurde. Wenn eine der 
vorhergehenden Zeilen ein #include war, dann in dieses Include File 
hinein und ganz unten mit der Rückwärtssuche fortfahren.

von zerleger (Gast)


Lesenswert?

Wie heißt nochmal das "Ding" was mit Dreiecken einen Körper simulieren 
kann ?
Ob das wohl das eigentliche ...

von Mark B. (markbrandis)


Lesenswert?

Karl H. schrieb:
> :-)
>
> Wenn du seltsame unerklärliche Fehlermeldungen bekommst, dann immer von
> der Stelle des Auftretens des Fehlers rückwärts suchen ob in einer(!)
> der vorhergehenden Zeilen ein ; vergessen wurde.

Oder eine geschweifte Klammer, oder eine runde Klammer, oder eine 
eckige, ...

Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen 
nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von 
öffnenden und schließenden Klammern feststellt, könnte man eine 
entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man 
dann zehntausend Fehlermeldungen auf einmal ;-)

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

Mark B. schrieb:
> Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen
> nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von
> öffnenden und schließenden Klammern feststellt, könnte man eine
> entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man
> dann zehntausend Fehlermeldungen auf einmal ;-)

Mein Reden!!! :D

von Karl H. (kbuchegg)


Lesenswert?

J. T. schrieb:
> Mark B. schrieb:
>> Ich habe nie verstanden, warum der Compiler deren Anzahl beim Übersetzen
>> nicht einfach mitzählt. Sobald man eine unterschiedliche Anzahl von
>> öffnenden und schließenden Klammern feststellt, könnte man eine
>> entsprechende Fehlermeldung ausgeben. Aber nein, stattdessen kriegt man
>> dann zehntausend Fehlermeldungen auf einmal ;-)

Das Problem ist, dass das nicht so einfach ist.
Was für dich eine einfache Sache ist, die du durch hinschauen löst, ist 
für den Compiler keineswegs offensichtlich. In
1
1 void foo()
2
2 {
3
3   if( i == 9 ) {
4
4 }
5
5
6
6 void bar()
7
7 {
8
8 }
ist für dich durch hinsehen offensichtlich, dass in Zeile 6 offenbar 
eine neue Funktion anfängt wodurch klar ist, dass in der vorhergehenden 
Funktion eine schliessende Klammer fehlen muss.

Für den Compiler sieht die Sache aber ganz anders aus. Für ihn 
präsentiert sich die Sache so, dass die schliessende Klammer in Zeile 4 
zur öffnenden Klammer in Zeile 3 gehören muss. D.h. Er sieht erst mal 
nur
1
void foo()
2
{
3
  if( i == 9 ) {
4
  }
5
6
  void bar

und dieses hier auftauchende void kann nicht an dieser Stelle stehen, da 
es hier syntaktisch nicht zulässig ist. Das die Fortsetzung dann 
suggerieren würde, dass es sich hier eigentlich um die versuchte 
Deklaration einer Funktion handelt, das kriegt der Compiler gar nicht 
mehr mit.

Fehlererkennung und sinnvolle Fehlermeldungen zu generieren ist 
erstaunlicherweise in einem Compiler eine ziemlich schwierige Sache. 
Solange man es mit einer zeilenorientierten Sprache zu tun hat, geht es 
ja noch. Denn da kann man wenigstens davon ausgehen, dass man alles in 
einer Zeile beisammen hat. Hat man dann erst mal ermittelt, was die 
übergeordnete Bedeutung der Zeile sein könnte (zb die Definition einer 
Funktion), dann folgt daraus schon sehr viel. Wenn aber 
Freiformformatierung zulässig ist, wie in C, kann es schon schwierig 
werden aus den nächsten n Symbolen zu erkennen, was hier wohl gemeint 
sein könnte. Zudem gibt es in C einige syntaktische Probleme, die die 
Sache nicht gerade vereinfachen. Gerade Dinge wie "man kann bei einer 
Strukturdefinition auch gleich Variablen von diesem Typ anlegen" sind 
anfällig für derartige Dinge.

ist in
1
struct vect
2
{
3
  int x;
4
  int y;
5
} int ...
nach der Strukturdefinition ein ; vergessen worden, oder hat der 
Programmierer einen Tippfehler gemacht und die Variable sollte gar nicht 
int heissen? Ist es so geschrieben
1
struct vect
2
{
3
  int x;
4
  int y;
5
} a, int, b;

dann ist ein Tippfehler naheliegend. Ist es aber so geschrieben
1
struct vect
2
{
3
  int x;
4
  int y;
5
} a int b;
dann hat er wohl einen ; vergessen und das sollte eigentlich
1
struct vect
2
{
3
  int x;
4
  int y;
5
} a;
6
7
int b;
lauten. Unterschied: ein lausiges Komma zwischen dem a und dem int.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Das was Du sagst ist schon alles richtig, Karl Heinz. Nichtsdestotrotz: 
Die Zahl der öffnenden und schließenden Klammern in einer einzelnen 
Datei zu zählen, das sind eine Handvoll Zeilen Code. Zumindest als eine 
Art "optionale Plausibilitätsprüfung" könnte man das einbauen.

von Karl H. (kbuchegg)


Lesenswert?

Mark B. schrieb:
> Das was Du sagst ist schon alles richtig, Karl Heinz. Nichtsdestotrotz:
> Die Zahl der öffnenden und schließenden Klammern in einer einzelnen
> Datei zu zählen, das sind eine Handvoll Zeilen Code. Zumindest als eine
> Art "optionale Plausibilitätsprüfung" könnte man das einbauen.

Klar könnte man.
Dann steht ganz unten: Die Anzahl schliessender Klammern stimmt nicht 
mit der Anzahl öffnender Klammern überein. Bringt dir das was? Nicht 
wirklich.

von Mark B. (markbrandis)


Lesenswert?

Karl H. schrieb:
> Klar könnte man.
> Dann steht ganz unten: Die Anzahl schliessender Klammern stimmt nicht
> mit der Anzahl öffnender Klammern überein. Bringt dir das was? Nicht
> wirklich.

In vielen Fällen (okay, nicht in allen) befinden sich die Klammernpaare 
in der gleichen Zeile (bei runden und eckigen Klammern). Wenn einen der 
Compiler optional darauf hinweisen könnte wo dies ist, könnte man sich 
einiges an Rumsuchen ersparen.

Bei geschweiften Klammern wird es zugegeben schwieriger.

Nichtsdestotrotz finde ich aussagekräftigere Fehlermeldungen gut. Ist 
hier nicht auch Clang/LLVM besser als gcc? Meine da sowas gelesen zu 
haben, wobei das wohl in Bezug auf Templates war. Da kriegt man ja 
teilweise wirklich kryptischen Schrott (sorry aber ist so) um die Ohren 
geworfen als Programmierer ;-)

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Mark B. schrieb:
> Nichtsdestotrotz finde ich aussagekräftigere Fehlermeldungen gut. Ist
> hier nicht auch Clang/LLVM besser als gcc?

In vielen Fällen ja, so auch hier:

Clang:
1
./GrafikTest_Init.h:49:75: error: expected ';' after top level declarator
2
sDreieck DreiecksVerschiebung(sKoordinate Schiebevektor, sDreieck Dreieck)
3
                                                                          ^
4
                                                                          ;

Der GCC lässt sich etwas an der Nase herumführen, weil er glaubt, es
handle sich bei der Funktionsdeklaration um eine fehlerhafte
Funktionsdefinition im K&R-Stil, wo die Argumenttypen nach der
schließenden runden Klammer deklariert werden. Dass seine Annahme falsch
ist, erkennt er erst am Ende von main.c, nachdem er breits 5 andere
Fehlermeldungen ausgegeben hat und der User schon lange nicht mehr
mitliest. Er springt dann wieder in GrafikTest_Init.c zurück und
erwischt dort immerhin die richtige Zeile:

GCC:
1
...
2
In file included from main.c:4:0:
3
GrafikTest_Init.h:49:10: error: old-style parameter declarations in prototyped function definition
4
 sDreieck DreiecksVerschiebung(sKoordinate Schiebevektor, sDreieck Dreieck)
5
          ^

Auf die Idee, dass in dieser Zeile einfach ein Semikolon fehlen könnte,
kommt er aber trotzdem nicht.

von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

(xxx)gcc --version ?

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.