Linie mit Pfeilende zeichnen (Formel)



  • Hallo zusammen, es soll eine Linie mit pfeilförmigem Ende gezeichnet werden. Koordinatenursprung ist die linke obere Ecke des Monitors. Startpunkt der Linie beliebig, Endpunkt ebenfalls beliebig aber innerhalb des Sichtbereichs des Monitors.

    Im ersten Bild eine Skizze mit drei verschiedenen Pfeilen welche der einfachheit halber alle vom selben Startpunkt beginnen. Ich habe versucht eine allgemeine Formel herzuleiten, aber die stimmt nur wenn der Pfeil von P1 nach P2 geht, für die anderen Pfeile sind die Werte für die Pfeilenden nicht korrekt.

    Im zweiten Bild die Herleitung damit verständlicher wird was ich meine.

    Was ich suche ist eine allgemeingültige Formel womit ich die Pfeilenden zeichnen kann, die Linie zu zeichnen ist ja nicht das Problem.

    Edit: Warum kann man denn hier keine Bilder uploaden?



  • Linie zeichnen: Bresenham's line algorithm

    Pfeil zeichnen: Orthogonal, dann 135° links, dann 90° links, und dann nochmal 135° links (Winkelsumme 360, passt). Fertig. Kein Zauberding.



  • Eh, 45+90+45=180, passt. Hab mich verrechnet gerade.



  • @Fragender Der Algorithmis ist nicht das was ich suche, zumindest verstehe ich dein Beispiel nicht.

    Das Problem ist nicht die Linien zu zeichnen, dafür reichen zwei Koordinaten und die GUI-Library zeichnet dann die Linie. Das Problem ist ans Ende der Linie eine Pfeilspitze (durch zwei weitere kurze Linien) dran zu zeichnen.

    @Admin warum kann ich hier im Forum keine Bilder anhängen? Ich würde gerne zwei Grafiken zum besseren Verständnis anhängen.



  • @sirEgbert sagte in Linie mit Pfeilende zeichnen (Formel):

    Das Problem ist nicht die Linien zu zeichnen, dafür reichen zwei Koordinaten und die GUI-Library zeichnet dann die Linie. Das Problem ist ans Ende der Linie eine Pfeilspitze (durch zwei weitere kurze Linien) dran zu zeichnen.

    Ja, und das habe ich dir erklärt. Ne Pfeilspitze ist nichts anderes als ein Dreieck.



  • @Fragender sagte in Linie mit Pfeilende zeichnen (Formel):

    Ne Pfeilspitze ist nichts anderes als ein Dreieck.

    Und diese Aussage jetzt allgemeingültig in einer mathematischen Formel ausgedrückt ist die Lösung die ich suche :-). Ich brauche Koordinaten der Pfeilspitzenlinien vom Endpunkt der Linie ausgehend.

    Wenn ich hier Bilder uploaden könnte, aber das geht irgendwie nicht.



  • Das wäre so das einfachste, was mir so einfallen würde, vielleicht hilft es dir ja.

    Java-Code:

    package org.example;
    
    import java.awt.geom.Point2D;
    import java.util.Scanner;
    
    public class Pfeile {
        public static Point2D add(Point2D a, Point2D b) {
            return new Point2D.Double(a.getX() + b.getX(), a.getY() + b.getY());
        }
    
        public static Point2D subtract(Point2D a, Point2D b) {
            return new Point2D.Double(a.getX() - b.getX(), a.getY() - b.getY());
        }
    
        public static Point2D multiply(Point2D a, double d) {
            return new Point2D.Double(a.getX() * d, a.getY() * d);
        }
    
        public static Point2D rotateDegrees(Point2D a, double deg) {
            final double rad = Math.toRadians(deg);
            return new Point2D.Double(a.getX() * Math.cos(rad) - a.getY() * Math.sin(rad),
                                      a.getX() * Math.sin(rad) + a.getY() * Math.cos(rad));
        }
    
        public static void main(String[] args) {
            System.out.println("Gib den Startpunkt und den Endpunkt (Pfeilspitze) ein:");
            String[] sa = new Scanner(System.in).nextLine().split(",? ");
            Point2D start = new Point2D.Double(Double.parseDouble(sa[0]), Double.parseDouble(sa[1]));
            Point2D end = new Point2D.Double(Double.parseDouble(sa[2]), Double.parseDouble(sa[3]));
            Point2D startEnd = subtract(end, start);
            Point2D end2 = add(start, multiply(startEnd, 0.9));
            System.out.println("Zeichne eine Linie von " + start + " bis " + end2);
            Point2D a1 = rotateDegrees(startEnd, 90);
            Point2D b1 = add(end2, multiply(a1, 0.05));
            Point2D a2 = rotateDegrees(startEnd, 270);
            Point2D b2 = add(end2, multiply(a2, 0.05));
            Point2D b3 = end;
            System.out.println("Zeichne eine Linie von " + b1 + " bis " + b2);
            System.out.println("Zeichne eine Linie von " + b2 + " bis " + b3);
            System.out.println("Zeichne eine Linie von " + b3 + " bis " + b1);
        }
    }
    
    
    Gib den Startpunkt und den Endpunkt (Pfeilspitze) ein:
    50, 50, 150, 140
    Zeichne eine Linie von Point2D.Double[50.0, 50.0] bis Point2D.Double[140.0, 131.0]
    Zeichne eine Linie von Point2D.Double[135.5, 136.0] bis Point2D.Double[144.5, 126.0]
    Zeichne eine Linie von Point2D.Double[144.5, 126.0] bis Point2D.Double[150.0, 140.0]
    Zeichne eine Linie von Point2D.Double[150.0, 140.0] bis Point2D.Double[135.5, 136.0]
    

    Und es sollte dann so aussehen:

    https://i.postimg.cc/2SmJRQBf/grafik.png



  • Schlussendlich ist das aber Geometrie ca. Klasse 11. Das soll aber kein Vorwurf sein.

    Falls die Pfeilspitze bzw. das Pfeilende ausgefüllt sein soll: https://de.wikipedia.org/wiki/Scanline-Algorithmus

    @sirEgbert sagte in Linie mit Pfeilende zeichnen (Formel):

    Wenn ich hier Bilder uploaden könnte, aber das geht irgendwie nicht.

    Ja, Bilder sind (in Datenbanken) böse ... Ich würde so eine Funktion aber auch begrüßen.

    Einstweilen kannst du https://postimages.org/de/ nutzen.


  • Mod

    @sirEgbert sagte in Linie mit Pfeilende zeichnen (Formel):

    Und diese Aussage jetzt allgemeingültig in einer mathematischen Formel ausgedrückt ist die Lösung die ich suche :-). Ich brauche Koordinaten der Pfeilspitzenlinien vom Endpunkt der Linie ausgehend.

    Linie sei von A nach B (was jeweils 2D-Koordinaten sind), Pfeilspitze soll an B. w sei der gewünschte Winkel des Pfeils, r die gewünschte Länge. Dann ist C=(A-B)/(Länge(A-B))*r ein Vector der Länge r in die Richtung der Grundlinie. Diesen wollen wir einmal nach +w und einmal nach -w drehen, also jeweils die zugehörige Drehmatrix dranmultipliziert. Zu den beiden Ergebnissen müssen wir dann noch die Spitze (also B ) addieren, dann haben wir die Koordinaten der beiden Endpunkte und zeichnen zu diesen Linien von B ausgehend.



  • @SeppJ Vielen Dank. Ich denke, er suchte eine textuelle Beschreibung - und keinen Pseudocode ...

    Allerdings würde ich die Vektoren Stütz-/Auf- und Richtungsvektoren nennen.



  • So danke euch für die Erklärungen, im Grunde habe ich auch an Vektorrechnung gedacht, habe aber Auffrischungsbedarf ^^

    Anbei zwei Links zu den Bildern:

    3 Vektoren mit Pfeilspitze

    Herleitung Formel

    Der Vektor b (in der Herleitung) ist das Ergebnis.

    Allerdings scheint mir die Herleitung nur für den Vektor a zu funktionieren, nicht aber für die anderen beiden. Wäre super wenn ihr mich da korrigieren könntet.



  • In welchen Kontext brauchst du das denn? Schule, Lineare Algebra-Vorlesung, ...?



  • @Fragender

    Rein privates Hobby, ist Aufgabe 3 aus Kapitel 13 von dem Programmierbuch von Stroustrup (Programming Principles). Ich will das einfach hinbekommen.

    Ernüchterndes Ergebnis

    ...sehr ernüchternd, die oberen beiden Linien sind zwar nicht ganz korrekt aber sehen nicht so schlecht aus, bei den beiden unteren ist der Pfeil verkehrt herum.

    Und hier der Code:

            Arrow a1{ Point{200,200}, Point{300,200} };
    	Arrow a2{ Point{200,200}, Point{300,100} };
    	Arrow a3{ Point{200,300}, Point{100,300} };
    	Arrow a4{ Point{200,300}, Point{100,400} };
    
    void Arrow::draw_lines() const
    {
    	double angle{ 45 };		// Winkel des Pfeils zur Linie
    	double length{ 20 };	// Länge des Pfeils
    	
    	// Winkel zwischen der Linie und der X-Achse
    	double alpha{ atan(static_cast<double>(p2.y - p1.y) / static_cast<double>(p2.x - p1.x)) };
    	double phi{ 90.0 - alpha - angle };		// Hilfswinkel: zwischen Pfeil und y-Achse
    
    	// Koordinaten für Pfeillinien (obere Seite) 
    	double px1{ p2.x - length * sin(phi) };
    	double py1{ p2.y - length * cos(phi)};
    
    	// Koordinaten für Pfeillinien (untere Seite)
    	double px2{ p2.x - length * cos(-phi) };
    	double py2{ p2.y - length * sin(-phi) };
    
    	// Normale Linie zeichnen
    	fl_line(p1.x, p1.y, p2.x, p2.y);
    	
    	// Pfeile oben/unten zeichnen
    	fl_line(p2.x, p2.y, px1, py1);
    	fl_line(p2.x, p2.y, px2, py2);
    }
    

  • Mod

    Du denkst schon dran, dass trigonometrische Funktionen in C++ (und so ziemlich bei jedem Kontext außerhalb der Schule) im Bogenmaß rechnen?

    Ansonsten würde ich meine Methode empfehlen. Was soll die x-Achse und der Winkel zu dieser mit dem Problem zu tun haben? Du willst doch einzig und alleine die Linie einmal um +phi und einmal um -phi drehen. Nix mit Achsen.



  • @sirEgbert Ich hatte dir doch schon Pseudocode bzw. Java-Code gezeigt. Ich glaube nicht, dass dieser noch weiter vereinfacht werden könnte...

    package org.example;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.geom.Point2D;
    import java.util.Scanner;
    
    public class Arrows {
        public static Point2D add(Point2D a, Point2D b) {
            return new Point2D.Double(a.getX() + b.getX(), a.getY() + b.getY());
        }
    
        public static Point2D subtract(Point2D a, Point2D b) {
            return new Point2D.Double(a.getX() - b.getX(), a.getY() - b.getY());
        }
    
        public static Point2D multiply(Point2D a, double d) {
            return new Point2D.Double(a.getX() * d, a.getY() * d);
        }
    
        public static Point2D rotateDegrees(Point2D a, double deg) {
            final double rad = Math.toRadians(deg);
            return new Point2D.Double(a.getX() * Math.cos(rad) - a.getY() * Math.sin(rad),
                    a.getX() * Math.sin(rad) + a.getY() * Math.cos(rad));
        }
    
        public static void drawArrow(Point2D start, Point2D end2, Point2D a, Point2D b, Point2D c) {
            JFrame frame = new JFrame();
            JPanel panel = new JPanel() {
                private void drawLine(Graphics g, Point2D a, Point2D b) {
                    g.drawLine((int) a.getX(), (int) a.getY(), (int) b.getX(), (int) b.getY());
                }
                @Override
                public void paint(Graphics g) {
                    drawLine(g, start, end2);
                    drawLine(g, a, b);
                    drawLine(g, b, c);
                    drawLine(g, c, a);
                }
            };
            frame.add(panel);
            frame.setSize(400, 400);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            System.out.println("Gib den Startpunkt und den Endpunkt (Pfeilspitze) ein:");
            String[] sa = new Scanner(System.in).nextLine().split(",? ");
            Point2D start = new Point2D.Double(Double.parseDouble(sa[0]), Double.parseDouble(sa[1]));
            Point2D end = new Point2D.Double(Double.parseDouble(sa[2]), Double.parseDouble(sa[3]));
            Point2D startEnd = subtract(end, start);
            Point2D end2 = add(start, multiply(startEnd, 0.9));
            System.out.println("Zeichne eine Linie von " + start + " bis " + end2);
            Point2D a = add(end2, multiply(rotateDegrees(startEnd, 90), 0.06));
            Point2D b = add(end2, multiply(rotateDegrees(startEnd, 270), 0.06));
            Point2D c = end;
            System.out.println("Zeichne eine Linie von " + a + " bis " + b);
            System.out.println("Zeichne eine Linie von " + b + " bis " + c);
            System.out.println("Zeichne eine Linie von " + c + " bis " + a);
            drawArrow(start, end2, a, b, c);
        }
    }
    

    https://i.postimg.cc/fTsGJdZ6/grafik.png



  • Ja, ok ... bei den Punkten a und b könntest du auch zuerst multiplizieren, dann drehen, und schließlich addieren. Da geht es aber schon Richtung Mikrooptimierung.



  • @sirEgbert
    Nur zur Info: du musst um die Pfeilspitze zu zeichnen den Winkel des Schafts nicht bestimmen. Du kannst einfach den Schaft-Vektor auf die gewünschte Länge bringen, und dann mit einer passenden Matrix multiplizieren um ihn um den gewünschten Winkel zu drehen: https://en.wikipedia.org/wiki/Rotation_matrix

    Der Vorteil davon ist dass du den Sonderfall p2.x == p1.x nicht mehr extra behandeln musst. (Abgesehen davon kannst du die Matritzen auch vorberechnen, was das ganze schneller macht, da du dann überhaupt keine Winkelfunktionen zur Laufzeit mehr brauchst. Wobei das vermutlich in den meisten Programmen keine Rolle spielt.)



  • @hustbaer sagte in Linie mit Pfeilende zeichnen (Formel):

    (Abgesehen davon kannst du die Matritzen auch vorberechnen, was das ganze schneller macht, da du dann überhaupt keine Winkelfunktionen zur Laufzeit mehr brauchst. Wobei das vermutlich in den meisten Programmen keine Rolle spielt.)

    Das geht aufgrund der Kommutativität der Matrizenmultiplikationen tatsächlich erstaunlich gut. Das durfte ich auch für 3d in einer Klausur berechnen/aufstellen. 🙂

    Allerdings braucht er dann hier 5 Matrizen für den kompletten Pfeil ... oder? Jap, so ist es.

    @hustbaer Auch die trigonometrischen Funktionen (sin, cos, usw.) sind inzwischen "vorberechnet"/gemappt. 🙂 Fallen also kaum noch ins Gewicht. Aber das wusstest du sicher schon... 😉



  • Oups... die sind natürlich nicht kommutativ, können aber dennoch wie ganz richtig beschrieben schön zusammengefasst werden.


Anmelden zum Antworten