Linie orthogonal zu bestehender Linie

Hallo zusammen,

ich komm gerade nicht weiter:

Ich hab ein Panel in dem ich mit der Maus zwei beliebige Punkte anklicke/markiere. Zwischen den zwei Punkten wird darauf hin eine Linie (später “erste Linie” genannt) gezeichnet. Alles noch kein Problem.

Nachdem die erste Linie nun gezeichnet wurde, soll zwischen der aktuellen Mausposition (sofern innerhalb des Panels) und der ersten Linie, eine zur ersten Linie orthogonale Linie eingezeichnet werden.

Sofern die Maus in solch einer ungeschickten Position steht, dass zur ersten Linie keine orthogonale Linie gezeichnet werden kann, weil die erste Linie zu kurz ist, dann soll in Verlängerung zur ersten Linie die orthogonale Linie erscheinen…

Ich hab grad gar keinen Plan wie ich das umsetze.

Was ich hinbekommen habe, mir aber nicht weiter hilft:

  • Auf die erste Linie eine orthogonale Linie zeichnen: Dazu habe ich die Linie dupliziert in in ihren Zentrum um 90° gedreht.

Aber ich müsste nun die orthogonale auf der ersten Linie so verschieben dass sie irgendwann in beliebiger Verlängerung den Mauspunkt schneidet…

Any ideas?

P.S. Hab noch eine Skizze angehängt. Die erste Linie ist zwischen P1 und P2. P3 ist die Maus. Gesucht ist für beliebige P1, P2 und P3 die orthogonale Linie die in der Skizze von P3 nach unten auf die Verlängerte Linie P1<->P2 reicht.

Mathematik ist wohl nicht wegzudenken als Begriff hier,
hast du keinerlei sonstige Vorgaben/ Wünsche, nur ‚soll gehen‘?

wenn man es sich frei aussuchen kann, dann ja wohl gleich den offiziellen Weg:
deine Zeichenfläche braucht eine Hinterlegung mit einem 2D-Koordinatensystem,
jeder Punkt ist irgendein x,y-Paar (was sonst sowieso?)

aus den bisherigen zwei Punkten hast du eine Gerade, mit einer Formel a la y = x+1
(rote Linie in Link http://www.xnamag.de/forum/viewtopic.php?t=2674 )

mit einem neuen Punkt und 90 Grad-Spiegelung kann man die andere Gerade eindeutig bestimmen
(Details noch nachzuschlagen :wink: , edit: Steigung ist der Mulitplikator am x, der gegebenen Punkt bestimmt den Plus-Term, Verschiebung der Geraden)

„Dazu habe ich die Linie dupliziert in in ihren Zentrum um 90° gedreht“ ist bisher allein Java-graphische Spielerei ohne mathematischen Hintergrund?

dann noch Schnittpunkt der Geraden berechnen, von/ bis dort auf der neuen Geraden malen

[QUOTE=SlaterB]Mathematik ist wohl nicht wegzudenken als Begriff hier,
hast du keinerlei sonstige Vorgaben/ Wünsche, nur ‚soll gehen‘?
[/quote]

Außer „soll gehen“ gibt’s nur noch „soll nicht extrem langsam sein“. Mehr dazu unten.

wenn man es sich frei aussuchen kann, dann ja wohl gleich den offiziellen Weg:
deine Zeichenfläche braucht eine Hinterlegung mit einem 2D-Koordinatensystem,
jeder Punkt ist irgendein x,y-Paar (was sonst sowieso?)

Punkte mit x/y und Koordinatensystem hab ich schon. Ist ja mit Swing soweit kein Problem.

aus den bisherigen zwei Punkten hast du eine Gerade, mit einer Formel a la y = x+1
(rote Linie in Link http://www.xnamag.de/forum/viewtopic.php?t=2674 )

Eine Formal hab ich nicht. Der Benutzer „klickt“ sich zwei Punkte im Panel und die Implementierung zeichnet dazwischen eine Linie. Bis jetzt noch pipifax.

mit einem neuen Punkt und 90 Grad-Spiegelung kann man die andere Gerade eindeutig bestimmen
(Details noch nachzuschlagen :wink:

So einfach ist es wohl nicht. Und wenns mit „mal kurz Nachschlagen“ erledigt wäre, hätte ich das schon getan. Stehe aber gerade wie oben schon geschrieben auf dem Schlauch und bräuchte einen anstupser…

, edit: Steigung ist der Mulitplikator am x, der gegebenen Punkt bestimmt den Plus-Term, Verschiebung der Geraden)

Bis jetzt noch Bahnhof… Den Winkel der geraden kann ich bestimmen. Kein Problem. Darauf eine orthogonale zeichnen ist auch kein Thema. Aber diese orthogonale auf der Linie zu verschieben und zu verlägern so dass ich den Mauspunkt treffe… Hier klemmts.

„Dazu habe ich die Linie dupliziert in in ihren Zentrum um 90° gedreht“ ist bisher allein Java-graphische Spielerei ohne mathematischen Hintergrund?

Korrekt. Es geht allein um das graphische. Formeln und Co. sind nicht das Ziel. Aber wenn sie das Mittel sind um zum Ziel zu kommen, dann eben auch Formeln.

Ich habe eine neue Skizze gemalt:

Gegeben ist P1, P2, die Linie zwischen P1<->P2, sowie P3.

Gesucht ist P4, so dass die Linie zwischen P3<->P4 im 90° Winkel zur Linie P1<->P2, sowie P4 entweder auf der Linie P1<->P2 oder in der Verlängerung davon.

P1 und P2 werden vom Benutzer beliebig gesetzt. P3 kommt nach dem setzten von P1 und P2 hinzu und entspricht immer der aktuellen Mausposition im Panel.

Die Frage ist jetzt: Wie komm ich an P4? Eine orthogonale auf die Linie P1<->P2 zu zeichnen ist kein Problem. Aber das mit P3 und dem gesuchten P4 zu verheiraten… daran scheitere ich gerade.

Ich will keine fertige Lösung (aber wenn sie jemand hat: gerne…). Ein konstruktiver Vorgehenshinweis wäre aber hilfreich.

[update]

Gerade den Hinweis bekommen „Abstand Punkt Gerade“ soll das Problem lösen: Abstand: Punkt zu Gerade

Mal schauen ob ich damit weiter komme.

Winkel muss wohl nicht sein,
lieber Geradensteigung
http://www.arndt-bruenner.de/mathe/9/geradedurchzweipunkte.htm
f(x) = mx + b

Orthogolalensteigung:
http://www.ina-de-brabandt.de/analysis/lin/gerade2d-orthogonal.html
also -1/m von f(x)

dann das b so verschieben, dass der neue Punkt getroffen wird bzw. ausrechnen

m2 vorhanden, Punkt yP,yP bekannt?
→ yP = m2 * xP + b2
→ b2 ausrechnen

damit die zweite Gerade fertig, dann den Schnittpunkt, da dürfte der erste Link gut sein
http://www.xnamag.de/forum/viewtopic.php?t=2674

oder alles noch neu suchen


Sonderfälle genau waagerechter/ senkrechter Geraden bedenken,
was anderes einfacheres nicht ausgeschlossen :wink:

Bei dem Wort „Steigung“ graust’s mir in solchen Zusammenhängen immer. Und zum Glück kommt man da ohne Schnittpunktberechnungen aus: Man muss nur das Lot vom Mauspunkt auf die Linie zwischen P1 und P2 fällen („drop the perpendicular“).

Dazu muss man

  • Den (normalisierten) Richtungsvektor der Linie ausrechnen (also Differenz der Endpunkte, geteilt durch die Länge)
  • Den Vektor vom Startpunkt der Linie zum Mauspunkt berechnen
  • Diesen Vektor auf den normalisierten Richtungsvektor projizieren
    – Den Abstand dieses projizierten Punktes zum Startpunkt der Line erhält man durch Berechnung des dot-Products aus dem Richtungsvektor und dem Vektor
    – Startpunkt + Abstand * Richtung = Projizierter Punkt
    → Das ist dann der „Lotfußpunkt“

Wenn man die Maus bewegt, bewegt man den Mauspunkt
Wenn man die Maus draggt, bewegt man einen Endpunkt der Linie

Sonderfälle … gibt es spontan nur einen, nämlich wenn die Line die Länge 0 hat. Was auch immer dann passieren soll, man muss sich schon Mühe geben, das hinzukriegen :smiley:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class Perpendicular
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    
    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        PerpendicularPanel panel = new PerpendicularPanel();
        f.getContentPane().add(panel);
        
        f.setSize(1000,800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class PerpendicularPanel extends JPanel implements MouseMotionListener
{
    private final Point2D point0;
    private final Point2D point1;
    private final Point2D point;
    
    PerpendicularPanel()
    {
        this.point0 = new Point2D.Double(600,500);
        this.point1 = new Point2D.Double(400,600);
        this.point = new Point2D.Double();
        addMouseMotionListener(this);
    }
    
    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        
        g.setColor(Color.BLACK);
        g.draw(new Line2D.Double(point0, point1));
        
        g.setColor(Color.RED);
        draw(g, point0);
        draw(g, point1);
        
        g.setColor(Color.GREEN);
        draw(g, point);
        
        Point2D result = dropPerpendicular(point0, point1, point);
        g.setColor(Color.BLUE);
        draw(g, result);
        
        g.setColor(Color.GRAY);
        g.draw(new Line2D.Double(point, result));
    }
    
    private static void draw(Graphics2D g, Point2D p)
    {
        Ellipse2D e = new Ellipse2D.Double(
            p.getX()-3, p.getY()-3, 6, 6);
        g.fill(e);
    }
    

    @Override
    public void mouseDragged(MouseEvent e)
    {
        point0.setLocation(e.getPoint());
        repaint();
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        point.setLocation(e.getPoint());
        repaint();
    }
    
    private static Point2D dropPerpendicular(
        Point2D p0, Point2D p1, Point2D p)
    {
        double dx = p1.getX() - p0.getX();
        double dy = p1.getY() - p0.getY();
        double length = Math.sqrt(dx*dx+dy*dy);
        double dirX = dx / length;
        double dirY = dy / length;
        double pdx = p.getX() - p0.getX();
        double pdy = p.getY() - p0.getY();
        double dot = dirX * pdx + dirY * pdy;
        double resultX = p0.getX() + dirX * dot;
        double resultY = p0.getY() + dirY * dot;
        return new Point2D.Double(resultX, resultY);
    }
}

Zufällig beim durchforsten der Formeln und wie man sie anwendet drauf gestoßen:

h**p://docs.oracle.com/javase/7/docs/api/java/awt/geom/Line2D.html#ptLineDist(java.awt.geom.Point2D)

Denke das ist das was ich suche … Damit hab ich den Orthogonalen Abstand zwischen P3 und der Linie P1<->P2 …

Ausgehend von P3 eine Linie mit der Länge und schon hab ich zusammen mit dem Winkel der Linie P1<->P2 den Punkt P4 gefunden…

Muss ich noch ausprobieren, aber ich denke das ist es…
@marco
Schau ich mir auch gleich an. Danke.

Das ganze wird fast schon trivial, wenn man das Problem vektororientiert angeht.
Einen orthogonalen Vektor bekommt man im 2D-Raum, indem man die Komponenten vertauscht und das Vorzeichen der einen Komponente invertiert.

Will heißen: Punkt A ist bei [tex](x_1, y_1)[/tex] Punkt B bei [tex](x_2, y_2)[/tex]. Diese Punkte bilden dann einen Vektor: [tex]\vec{a}=(x_1 - x_2, y_1 - y_2)^T[/tex] Ein orthogonaler Vektor dazu ist dann [tex]\vec{b}[/tex] mit [tex]\vec{b}=(y_2-y_1, x_1-x_2)^T[/tex].

Ja, diesen Vektor könnte man jetzt so verschieben, dass er beim Mauspunkt anfängt, und so skalieren, dass seine Länge dem Abstand Mauspunkt-Gerade entspricht. Aber wenn man sich mal anschaut, was z.B. in Line2D bei der Berechnung des Abstandes zwischen einem Punkt und einer Linie gemacht wird, wird man feststellen, dass das ziemlich genau die Punkte sind, die ich oben schon aufgelistet hab - nur wird der „Lotfußpunkt“ nicht zurückgegeben, sondern nur für die Abstandsberechnung verwendet :wink:

Sry, als ich den Beitrag geschrieben hatte, war noch keine Antwort da. Zwischendurch hatte ich noch eine Unterhaltung :wink:

Danke nochmal für deine Hilfe. Mit ptLineDist hätte ich’s wohl selbst hinbekommen. Aber dein dropPerpendicular war die exakte Lösung.

[QUOTE=Marco13]

        double dy = p1.getY() - p0.getY();
        double length = Math.sqrt(dx*dx+dy*dy);
        double dirX = dx / length;
        double dirY = dy / length;
        double pdx = p.getX() - p0.getX();
        double pdy = p.getY() - p0.getY();
        double dot = dirX * pdx + dirY * pdy;
        double resultX = p0.getX() + dirX * dot;
        double resultY = p0.getY() + dirY * dot;
```[/QUOTE]
die 'Steigung und y-Achsenabschnitt'-Variante ( ;) ) schlägt sich im Vergleich gar nicht so schlecht, von 999999999d abgesehen
```        double dx = p1.getX() - p0.getX();
        double dy = p1.getY() - p0.getY();
        double m = dx == 0 ? 999999999d : dy / dx;
        double b = p1.getY() - m * p1.getX();
        double m2 = m == 0 ? 999999999d : -1 / m;
        double b2 = p.getY() - m2 * p.getX();
        double resultX  = (b2 - b) / (m - m2);
        double resultY  = m * resultX  + b;
        this.point0 = new Point2D.Double(600,200);
        this.point1 = new Point2D.Double(600.000000000001,600);

:stuck_out_tongue:

kommt ja noch ganz gut hin :wink:

Ja, sicher, wirkt gekünstelt, aber… mal im Ernst: Man muss eben damit rechnen, dass bei 300.0+300.0 nicht 600.0, sondern 600.00000000001 rauskommt. Man könnte da jetzt noch mit if (dx < 10e-10) ... und so dengeln, aber … meine Faustregel bei allem, was mit Vektoren zu tun hat: Steigung = Bähbäh :wink:

hier nicht die Frage oder? double rechnest du beim Skalarprodukt ja auch

Problem ist die Steigerung von 10^14 und mehr, da kommt bei minimal genauen x von double y-Sprünge in mehreren Pixeln und größer raus,
mit den 999999999d für ‚unendliche Steigerung‘ hatte ich zufällig noch einen akzeptablen Wert gewählt,

Steigung darauf begrenzen oder mit if else jeweils die weniger dramatische der zwei Geraden wählen zur Schnittpunktberechnung, dann ginge es vorerst wieder,
aber klar, Abstand-Lösung schöner wenn ohne Probleme

Das bezog sich eher darauf, dass eine genaue Abfrage bei Doubles, also egal ob
if (someDouble == 0.0)
if (someDouble == someOtherDouble)
if (someDouble != 0.0) oder
if (someDouble != someOtherDouble)
selten bis nie Sinn macht (außer wenn 0 wirklich als „besonderer Wert“ gesetzt wird, aber eben nicht, wenn man das als Ergebnis einer Berechnung erwartet). Das driftet jetzt aber wohl schon etwas ab :wink: