Kollisionsproblem



  • Thx an D1BAKEL

    Aber du hast noch etwas vergessen: Die Reibungskraft. 😉
    Vielen Dank dass du dir die Mühe machst und mir hilfst.



  • Kein Problem, jetzt kommt auch was konstruktives (die Funktionen SetX() und SetY() müssen noch initialisiert werden und Ball_Radius gibts auch noch nichts):

    void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken )
    {
        for( int i = 0; i < BlocksAnzahl; ++i )
        {
            if( !daweg[i] ) continue;
    
            if( Blocks[i].RX() - Ball_Radius > Ball->RX() &&
                Blocks[i].RX() - Ball_Radius > Ball->RX() + Ball->StepX ) continue;
            if( Blocks[i].RY() - Ball_Radius > Ball->RY() &&
                Blocks[i].RY() - Ball_Radius > Ball->RY() + Ball->StepY ) continue;
            if( Blocks[i].RX() + Ball_Radius < Ball->RX() - Block_Width  &&
                Blocks[i].RX() + Ball_Radius < Ball->RX() - Block_Width  + Ball->StepX) continue;
            if( Blocks[i].RY() + Ball_Radius < Ball->RY() - Block_Height  &&
                Blocks[i].RY() + Ball_Radius < Ball->RY() - Block_Height + Ball->StepY) continue;
    
            daweg[i] = false;
            double yD, x;
    
            if( Ball->StepY ) yD = -Ball->RY() - Ball_Radius + Blocks[i].RY();
            else              yD =  Ball->RY() - Ball_Radius - Blocks[i].RY() - Block_Height;
    
            x = yD * Ball->StepX / Ball->StepY + Ball->RX();
    
            if( x < Blocks[i].RX() )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX;
                Ball->StepX *= -1;
            }
    
            else if(  x > Blocks[i].RX() + Block_Width )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX + 2 * Block_Width );
                Ball->StepX *= -1;
            }
    
            else
            {
                Ball->SetY( 2 *  yD + Ball->RY() - Ball->StepY );
                Ball->StepY *= -1;
            }
        }
    }
    

    Ich habs nicht getestet (wie auch), aber ich hab n gutes Gefühl, probiers aus, und sag obs funktioniert, bzw. wos Fehler gibt. Mach am besten nur einen einzigen Block und sieh dann ganz genau hin, obs ungenau ist. Diese Variante könnte weitere Fehler bergen, wenn der Ball zu schnell wird, probiers einfach aus...

    mfG D1B



  • Ok, zuerst einmal danke für den riesen Aufwand.
    Also es funzt leider nicht:
    Die Kollision wird richtig erkannt.
    Aber:
    So bald der Ball einen Block trifft, wird er an die falsche Stelle gesetzt.
    Die Richtung wird allerdings richtig verändert.
    Ich dachte mir jetzt dass es nur an dem yD liegen kann.
    Aber was ich nocht nicht verstehe ist, für was das yD überhaupt steht.
    Ich denke auch, dass du davon ausgehst, dass ich die Koordinaten des Mittelpunkts des Balles speichere. Das tue ich allerdings nicht.
    Ich komme darauf, da du bei der ersten Abfrage gleich von der X-Koordinate den Radius abziehst und dann mit der X-Koordinate des Balles vergleichst.
    Jetzt wüsste ich noch gerne, was du genau bei yD berechnest.
    Und warum du überhaupt
    if(Ball->StepY)
    abfrägst, denn der Ball kann sich, zumindest in meinem Programm nicht wagrecht bewegen, d.h. dass Ball->StepY nie gleich 0 sein kann.

    Nochmals vielen vielen Dank für den Aufwand.

    Ich würde micht freuen wenn du mir das mit dem yD noch erklären würdes.

    Thx



  • Also yD ist jeweils die Entfernung von der Position des Balles bis zum Objekt. Wenn der Ball über dem Objekt ist, ist es die Entfernung senkrecht zur oberen Kante, wenn er darunter ist zur unteren Kante. Wenn dur mir noch sagt, wie du den Ball gespeichert hast, kann ich dir die Funktion noch verfeinern. yD ist von daher wichtig, dass ich den Schnittpunkt über den zweiten Strahlensatz berechne, das bietet sich an, da die Blöcke alle in einem kartesischen Raster angeordnet sind. Hast du in deiner Ball-Struktur die untere linke Ecke gespeichert, oder welchen Punkt? Dann versteh ich auch, warum du mit dem Durchmesser gearbeitet hast, hätte mir eigentlich gleich auffallen sollen 🙄 Du hast gesagt, der Ball wird an die falsche Stelle gesetzt, ist diese Stelle weit von der richtigen Entfernt, oder liegt der Fehler nur in meiner falschen Auffassung von deinem Ball, ist die falsche Position als nur um einen Radius zur Seite und einen auf der Vertikalen verschoben, oder manchmal auch auf der ganz anderen Seite. If(Ball->StepY) ist dasselbe, wie if(Ball->StepY > 0), wenns also negativ ist, wird der elseZweig ausgeführt. Ich teste damit also, ob der Ball sich nach oben oder nach unten bewegt.

    Schreib mir also, wie du den Ball speicherst, und erkläre, was du mit "an der falschen Stelle" meinst, dann werde ich mir die Funktion nochmal angucken...

    mfG D1B



  • Also der Ball wird, wenn ziemlich genau in der Mitte des Blocks trifft richtig weiter geleitet, da ist es auch egal auf welcher Seite er trifft. Allerdings habe ich es bisher noch nicht geschafft, dass er von der unteren Seite richtig abprallt. Also geht es von jeder außer der unteren Seite, wenn er in der Mitte trifft. wenn er allerdings nicht in der Mitte trifft, dann hat er nach dem Treffer eine total falsche Position (kann bis zu 100 Pixel falsch sein, schätze ich mal).
    Ich speichere die Koordinaten der linken oberen Ecke meines Balles.
    Und das mit dem
    if(Ball->StepY)
    gut, dass ich das jetzt auch richtig weiß, denn so etwas kann man öfters gebrauchen.

    Vielen Dank nochmal



  • Gehe ich recht in der Annahme, dass dein KoordinatenUrsprung in der linken oberen Ecke ist? Ich bin nämlich von der unteren ausgegangen, werds nochma überdenken und überarbeiten...



  • Okay, dann versuchs mal hiermit (hab den Ball deinen Vorstellungen angeglichen und dafür gesorgt, dass er auch unten korrekt abprallt, hoffe ich jedenfalls...):

    void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken )
    {
        for( int i = 0; i < BlocksAnzahl; ++i )
        {
            if( !daweg[i] ) continue;
    
            if( Blocks[i].RX() > Ball->RX() &&
                Blocks[i].RX() > Ball->RX() + Ball->StepX ) continue;
            if( Blocks[i].RY() > Ball->RY() &&
                Blocks[i].RY() > Ball->RY() + Ball->StepY ) continue;
            if( Blocks[i].RX() < Ball->RX() - Block_Width  + Ball_Durchmesser &&
                Blocks[i].RX() < Ball->RX() - Block_Width  + Ball_Durchmesser + Ball->StepX) continue;
            if( Blocks[i].RY() < Ball->RY() - Block_Height + Ball_Durchmesser &&
                Blocks[i].RY() < Ball->RY() - Block_Height + Ball_Durchmesser + Ball->StepY) continue;
    
            daweg[i] = false;
            double yD, x;
    
            if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY();
            else              yD =  Ball->RY() - Blocks[i].RY() - Block_Height;
    
            x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2;
    
            if( x < Blocks[i].RX() )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser );
                Ball->StepX *= -1;
            }
    
            else if(  x > Blocks[i].RX() + Block_Width )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width );
                Ball->StepX *= -1;
            }
    
            else
            {
                Ball->SetY( ( Ball->StepX ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser );
                Ball->StepY *= -1;
            }
        }
    }
    

    Zwei Sachen noch: Zum ersten würde ich mir echt überlegen, den Ball nicht doch im Mittelpunkt zu speichern, evtl. auch die Blöcke, und von dort aus dann die halbe Länge, der beiden Achsen, mir gefällt das jedenfalls besser, da sich dann nur Vorzeichen ändern und man leichter den Überblich behält... Naja, deine Entscheidung. Und dann wundere ich mich, warum die Funktion Collision() eine MemberFunktion von deiner Struktur Block ist! Ich fände sie fiel einleuchtender, wenn die Funktion Member von Ball wäre, dann bräuchtest du den schon mal nicht als Parameter übergeben. Statt dessen kannst du dann lieber einen Pointer auf dein Array mit den Blöcken übergeben, wäre sehr viel Stilvoller. Wie rufst du diese Funktion denn auf? Gibt es da einen Block, den du favorisierst, der immer die Kollision übernehmen darf, oder lässt du das jeden Block in jedem Frame machen??? :p

    mfG D1B



  • Hallo,
    Als erstes einmal ich bin jetzt auch angemeldet, nur damit du weißt, dass es immer noch ich bin.
    Ok also dieser Code setzt jetzt die Koordinaten richtig, allerdings kommt es jetzt häufig vor, dass der Ball durch einen Block hindurchfliegt. Dabei liegt meine Geschwindigkeit nur bei 8.0 Pixel pro Frame.
    Die Richtungen berechne ich dan mit sin und cos.
    Und die Blockhöhe ist 20 Pixel und die Breite 40.
    Das mit der Kollisionserkennung kommt folgendermaßen zustande:
    Ich habe eine Klasse für alle Sprites als Manager geschrieben. und deshalb habe ich auch die Kollisionserkennung in der Blocksklasse.
    Die Kollision mit dem Brett habe ich z.B. in der Brettklasse.
    Ich hoffe du kannst mir noch weiter helfen mit meinem Problem.

    Auf jeden Fall noch einmal Großes Lob an dich.
    Vielen Dank.



  • Hallo,
    Also ich habe da gerade einen Fehler in deinen ersten if-Abfragen entdeckt:

    - bei der ersten frägst du nach der linken Seite ab, da muss der Ball_Durchmesser dazu
    - bei der zweiten frägst du nach der oberen Seite ab, da muss der Ball_Durchmesser ebenfalls dazu
    - bei der dritten frägst du nach der rechten Seite ab, da muss der Ball_Durchmesser nicht dazu
    - bei der vierten frägst du nach der unteren Seite ab, da muss der Ball_Durchmesser nicht dazu.

    Dies ist so, da ich ja die Koordinaten der linken oberen Ecke des Balles speichere.

    Das ist aber leider immer noch nicht die Lösung des Problems.

    Nochmal Vielen Dank

    Ich hoffe ich habe das richtig verstanden.

    [ Dieser Beitrag wurde am 24.02.2003 um 17:48 Uhr von Zero0Cool editiert. ]



  • Okay, dann hier mein nächster Vorschlag (Ball_Radius wird nicht mehr genutzt, kann also verworfen werden):

    void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken )
    {
        for( int i = 0; i < BlocksAnzahl; ++i )
        {
            if( !daweg[i] ) continue;
    
            if( Blocks[i].RX() < Ball->RX() - Block_Width  &&
                Blocks[i].RX() < Ball->RX() - Block_Width  + Ball->StepX ) continue;
            if( Blocks[i].RY() < Ball->RY() - Block_Height &&
                Blcoks[i].RY() < Ball->RY() - Block_Height + Ball->StepY ) continue;
            if( Blocks[i].RX() > Ball->RX() + Ball_Durchmesser &&
                Blocks[i].RX() > Ball->RX() + Ball_Durchmesser + Ball->StepX ) continue;
            if( Blocks[i].RY() > Ball->RY() + Ball_Durchmesser &&
                Blocks[i].RY() > Ball->RY() + Ball_Durchmesser + Ball->StepY ) continue;
    
            daweg[i] = false;
            double yD, x;
    
            if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY();
            else              yD =  Ball->RY() - Blocks[i].RY() - Block_Height;
    
            x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2;
    
            if( x < Blocks[i].RX() )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser );
                Ball->StepX *= -1;
            }
    
            else if(  x > Blocks[i].RX() + Block_Width )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width );
                Ball->StepX *= -1;
            }
    
            else
            {
                Ball->SetY( ( Ball->StepX ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser );
                Ball->StepY *= -1;
            }
        }
    }
    

    Ich hoffe, dass es jetzt klappt...

    mfG D1B



  • Ok vielen Dank.
    Aber es gibt leider immer noch das Problem mit dem SetX() und SetY(),
    denn wenn ein Block getroffen wird, dann wird der Ball an die falsche stelle gesetzt.
    Ich weiß allerdings nicht an was das liegen könnte. Denn wenn ich es nachrechne stimmt alles.
    Außer dem SetX() bzw. SetY() dort bekomme ich noch das falsche Ergebnis raus.
    Ich weiß allerdings noch nicht warum.
    Ich werde das dann noch einmal überprüfen.

    Vielen Dank nochmals für den Code.



  • Rekaptitulieren wir noch einmal: Die Funktionen RX() und RY() von Ball und Block liefern jeweils die linke obere Ecke. Probier doch mal folgendes: Setze den Ball mit SetX(0); SetY(0) auf den Koordinatenursprung. Dann sollten der linke und der obere Bildschirm(Viewport-)Rand Tangentem an den Ball sein, er ist also so weit in der Ecke, wie (physikalisch) möglich. Wenn das klappt, scheinen die Set-Funktionen zu funktionieren. Über das Ergebnis dieses Versuches solltest du mich umgehend informieren... Setze für diesen Versuch ruhig mal StepX und StepY auf Null, um genau gucken zu können.

    Gibt es denn noch Kollisionen, die er nicht erkennt, oder auf die er die Richtung in falscher Weise ändert? Wenn nicht ist das Problem fast gelöst...

    mfG D1B



  • Ok ich habe jetzt einmal die letzten Abfragen ein wenig umgeschrieben, da es passiert ist, dass der Ball von links oben gekommen ist, dann rechts oben den Block getroffen hat und dann nach links unten abgeprallt ist. Und das kann ja wohl nicht Sinn der Sache sein, oder?
    Ok hier jetzt einmal mein Verbesserungsvorschlag:

    if( x <= Blocks[i].RX() && Ball->StepX >= 0 )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser );
                Ball->StepX *= -1;
            }
    
            else if(  x >= Blocks[i].RX() + Block_Width && Ball->StepX <= 0 )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width );
                Ball->StepX *= -1;
            }
    
            else
            {
                Ball->SetY( ( Ball->StepY ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser);
                Ball->StepY *= -1;
            }
    

    Ok viel hat sich nicht geändert ist aber eben auch wichtig.
    Das mit dem SetX() und SetY() bekomme ich einfach nicht hin. 😞
    Bitte hilf mir.

    Thx

    Edit: Wenn ich bei SetX() und SetY() jeweils 0 angebe, dann befindet sich der Ball in der linken oberen Ecke des Bildschirms. und zwar so weit wie möglich.

    [ Dieser Beitrag wurde am 24.02.2003 um 19:48 Uhr von Zero0Cool editiert. ]



  • Poste doch mal bitte die Struktur namens Sprite, das würde uns sehr weiterhelfen...



  • > von links oben gekommen ist, dann rechts oben den Block getroffen hat und dann nach links unten abgeprallt

    Wie kannst du denn sehen, wo der Ball den Block trifft, dass passiert doch zwischen 2 Frames... 😕

    Nochwas: Weißt du eigentlich, was x ist?



  • Na ja also das mit dem Treffer da ich mir ja nur einen Block gemacht habe, kann ich schon ungefähr vorraus sagen, wo der Ball den Block treffen wird.
    Und da habe ich eben gesehen, dass der Ball den Block rechts oben treffen muss.
    Und trotzdem ist er nach links unten abgeprallt. Aber das ist jetzt nicht mehr.

    Das mit dem x ich denke, dass das x für die X-Koordinate steht, an der der Ball den Block treffen wird???

    Wenn es nicht so ist, dann kläre mich bitte auf...

    Ich weiß echt nicht mehr weiter...

    Thx@D1BAKEL



  • Oh Mann, ich glaube, jetzt haben wirs. Es gab da noch zwei kleine Fehler: Der erste ist passiert, als ich die ganzen Radien entfernt hab, da hab ich ein Vorzeichen übersehen und es weggenommen, statt nen Durchmesser draus zu machen. Und der zweite Fehler ist mir ja so richtig peinlich, ein simpler Tippfehler: In der letzten Abfage darfs natürlich nicht Ball->StepX heißen, sondern muss Ball->StepY heißen, man will ja die vertikale Richtung wissen...

    void Block::Collision( Sprite* Ball, PointManager* PM, DirectXAudio* DXAudio, Sprite* Funken )
    {
        for( int i = 0; i < BlocksAnzahl; ++i )
        {
            if( !daweg[i] ) continue;
    
            if( Blocks[i].RX() < Ball->RX() - Block_Width  &&
                Blocks[i].RX() < Ball->RX() - Block_Width  + Ball->StepX ) continue;
            if( Blocks[i].RY() < Ball->RY() - Block_Height &&
                Blcoks[i].RY() < Ball->RY() - Block_Height + Ball->StepY ) continue;
            if( Blocks[i].RX() > Ball->RX() + Ball_Durchmesser &&
                Blocks[i].RX() > Ball->RX() + Ball_Durchmesser + Ball->StepX ) continue;
            if( Blocks[i].RY() > Ball->RY() + Ball_Durchmesser &&
                Blocks[i].RY() > Ball->RY() + Ball_Durchmesser + Ball->StepY ) continue;
    
            daweg[i] = false;
            double yD, x;
    
            if( Ball->StepY ) yD = -Ball->RY() + Blocks[i].RY() - Ball_Durchmesser;
            else              yD =  Ball->RY() - Blocks[i].RY() - Block_Height;
    
            x = yD * Ball->StepX / Ball->StepY + Ball->RX() + Ball_Durchmesser / 2;
    
            if( x < Blocks[i].RX() )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser );
                Ball->StepX *= -1;
            }
    
            else if(  x > Blocks[i].RX() + Block_Width )
            {
                Ball->SetX( 2 * x - Ball->RX() - Ball->StepX - Ball_Durchmesser + 2 * Block_Width );
                Ball->StepX *= -1;
            }
    
            else
            {
                Ball->SetY( ( Ball->StepY ? 2 : -2 ) * yD + Ball->RY() - Ball->StepY + Ball_Durchmesser );
                Ball->StepY *= -1;
            }
        }
    }
    

    Ich möchte dich bitte, das gleich ma auszuprobieren, und mir zu sagen, obs jetzt endlich funktioniert, wenn nicht mit entsprechender Fehleranalyse... 🙄

    mfG D1B

    [ Dieser Beitrag wurde am 24.02.2003 um 20:19 Uhr von D1BAKEL editiert. ]



  • Und hier ist noch die Struktur Sprite...
    Na ja es ist eigentlich keine Struktur sonder eine Klasse.
    Aber ich denke du meinst diese?

    class Sprite
    {
    public:
        bool Bounce(IM* InputM, Sprite* Brett, DirectXAudio* DXAudio, Sprite* Ball);
        Sprite();
        void Create(DDClass* DDraw, char* Filename, int frmWidth, int frmHeight, int frmX, int frmY);
        void SetXY(int SpriteX, int SpriteY);
        void SetX(int SpriteX);
        void SetY(int SpriteY);
        void Move(int SpriteDX, int SpriteDY);
        void Drawhin(LPDIRECTDRAWSURFACE7 lpDDSurface);
        void Drawweg(LPDIRECTDRAWSURFACE7 lpDDSurface);
        void Drawloop(LPDIRECTDRAWSURFACE7 lpDDSurface);
        bool TestCollide(Sprite* Sprite);
        void Release();
        void ResetCurrentFrame();
        void ResetFrames(void) { CurrentFrame = 0; }
        int RX();
        int RY();
        int GetWidth(void)  { return FrameWidth;  }
        int GetHeight(void) { return FrameHeight; }
        double StepX;
        double StepY;
    
    private:
        LPDIRECTDRAWSURFACE7 lpDDSpriteSurface;
    
        int SpriteWidth;
        int SpriteHeight;
        int x;
        int y;
        int FrameWidth;    
        int FrameHeight;   
        int FrameDelay;    
        int FramesX;       
        int FramesY;       
        int CurrentFrameX; 
        int CurrentFrameY; 
        int delay;
        int CurrentFrame;
    };
    

    [ Dieser Beitrag wurde am 24.02.2003 um 20:21 Uhr von Zero0Cool editiert. ]



  • Es tut mir leid, aber der Code funktioniert immer noch nicht.
    Es funktioniert immer noch nicht.
    Aber was mich noch interessieren würde ist, was du eigentlich geändert has ich kann nämlich nichts finden...
    na ja auf jeden Fall siehts so aus, dass es eigentlich nur funktioniert, wenn der Ball die Oberkante des Blocks trifft. Ansonsten wird er an die falsche Stelle gesetzt.
    Ich komme einfach nicht darauf wo der Fehler liegt.



  • Okay, jetzt folgen einige Erklärungen:

    Die ersten vier if-Abfragen testen, ob sich der Ball vor und nach der Bewegung auf ein und derselben Seite (oben, unten, links oder rechts) vom Block befindet, wenn dieses zutrifft, gibt es keine Kollision und dieser Block kann übersprungen werden (continue;) Andernfalls gehe ich davon aus, dass er getroffen wird. Nach etwas Überlegung sollte auffallen, dass der Ball vor der Bewegung rechts vom Block sein kann, und nach der Bewegung über dem Block, so dass es trotzdem zu keiner Kollision kommt, diese aber fälschlicherweise ausgelöst wird. Dazu kommt es allerdings nur, wenn der Ball wirklich schnell ist und sehr klein.

    yD ist der Abstand vom Ball zum Block. Wenn der Ball also nach unten fliegt (Ball->StepY) speichert yD den Abstand von der Oberkante des Blockes zu der unteren Tangente des Balles, die parallel zur X-Achse liegt, andernfalls (else) speichert yD den (senkrechten) Abstand von der unteren Blockkante bis zur BallOberkante.

    Mithilfe des Strahlensatzes und der Größe yD wird x berechnet. Da die y-Koordinate des Balles links und nicht in der Mitte ist, muss noch ein halber Durchmesser addiert werden, um den tatsächlichen Schnittpunkt zu bekommen, dessen x-Koordinate in x gespeichert wird. In den folgenden Abfragen wird getestet, ob x überhaupt auf der Kante des Blockes liegt. Wenn x sich links vom Block befindet (x < Blocks[i].RX()) Kollidiert der Ball links, wenn x rechts liegt (x > Blocks[i].RX() + Block_Width), kollidiert er rechts. In beiden fällen wir StepX umgekehrt, nachdem der Ball an der entsprechenden (linken oder rechten) Kante gespiegelt wird (SetX(...)) zum elseZweig: Wenn x weder links noch rechts ist, hat der Ball den Block oben oder unten getroffen. Nun muss man auch hier spiegeln, da yD aber immer positiv ist, muss man nochmal unterscheiden, ob der Ball nach oben oder nach unten fliegt (Ball->StepY) und yD entsprechend addieren oder subtrahieren...

    mfG D1B

    PS: Um auf die genauen Formeln zu kommen mach ich mir immer Zeichnungen, hierfür sind 2 DinA4-Blätter draufgegangen... 😮


Anmelden zum Antworten