Point2D.Double Kurven - Beschriftung und richtiges Anklicken

CptK

Bekanntes Mitglied
Hallo zusammen,

ich habe immer noch ein Problem mit Linien vom Typ Path2D.Double. Dabei zeichne ich jede Linie durch 3 Punkte: Anfangspunkt, Endpunkt und Anker (Mittelpunkt):
Java:
line.moveTo(from.x(), from.y());
line.curveTo(from.x(), from.y(), anchor.x, anchor.y, to.x(), to.y());
line.moveTo(to.x(), to.y());
line.closePath();
g2.drawString(String.valueOf(Arrays.toString(letters)), anchor.x + 2, anchor.y + 2);
g2.draw(line);

In der Skizze weiter unten ist der Anker als schwarzer Punkt dargestellt. Wie man sieht, wird die Linie nicht direkt durch den Anker gezeichnet (ich weiß nicht wieso, es stört mich aber eigentlich auch nicht so wirklich), allerdings nutze ich den Anker auch um Text zu positionieren. Kann ich während dem Zeichnen der Linie den Punkt bekommen, durch den die Linie wirklich geht, sodass der Text am Ende an der Linie und nicht so weit weg steht?

Das zweite Problem ist das Erkennen, ob ein Mausklick auf der Linie ist oder nicht, was ich bisher wie folgt bestimme:
Java:
final int HIT_BOX_SIZE = 10;
Rectangle2D rectangle = new Rectangle(x - HIT_BOX_SIZE / 2, y - HIT_BOX_SIZE / 2, HIT_BOX_SIZE, HIT_BOX_SIZE);

return line.intersects(rectangle);
Wobei x und y für die Koordinaten des Klicks stehen. Bei einer geraden Linie funktioniert das auch einwandfrei, bei einer Kurve hingegen, wird auch bei einem Klick auf die Fläche unter der Linie (In der Abbildung unten grün) true zurückgegeben. Wie kann ich das so umschreiben, dass wirklich nur bei einem Klick auf die Linie true zurück geliefert wird?

1616927168903.png
 

LimDul

Top Contributor
Das du als Anker bezeichnest, ist kein Anker oder Mittelpunkt, sondern Teil einer Bezier Kurve:

Da sieht man auch schön, warum der Punkt nicht drauf liegt im Bild dort.

Anhand der Formeln lässt sich mit Sicherheit auch die Fläche darunter berechnen, bzw. berechnen ob ein Punkt innerhalb oder außerhalb liegt.
 

CptK

Bekanntes Mitglied
Das du als Anker bezeichnest, ist kein Anker oder Mittelpunkt, sondern Teil einer Bezier Kurve:

Da sieht man auch schön, warum der Punkt nicht drauf liegt im Bild dort.

Anhand der Formeln lässt sich mit Sicherheit auch die Fläche darunter berechnen, bzw. berechnen ob ein Punkt innerhalb oder außerhalb liegt.
das sieht vorsichtig ausgedrückt interessant aus...
Und sowas gibts nicht zufällig schon fertig bzw. in einer einfacheren Form, die für meine Zwecke reicht?
 

CptK

Bekanntes Mitglied
EH, ich will dich nicht irritieren, aber du zeichnest mit deinem Code bereits genau eine Bézierkurve...
Ja ja, das habe ich verstanden aber da gebe ich dem drei Punkte und der macht den Rest für mich ^^
Wenn ich da jetzt aber selbst berechnen muss, wo ich meinen Text hinschreiben soll oder ob ich jetzt auf die Linie geklickt habe oder daneben, bin ich raus
 

CptK

Bekanntes Mitglied
Also, ich habe jetzt eine Bezierkurve 2. Grades implementiert, wobei ich nach folgendem Schema vorgegangen bin:
1617033950655.png
Im folgenden Code werden die Schritte 1 - 4 durch diese Methoden umgesetzt:
Java:
1 -> calculateGraphBetweennPoints()
2 -> straightBetweenAnchorsX()
3 -> getFunction()
4 -> calculate()

Java:
public class BezierCurve {

    private Point2D anchor1;
    private Point2D anchor2;
    private Point2D controlPoint;

    public BezierCurve(Point2D anchor1, Point2D anchor2, Point2D controlPoint) {
        this.anchor1 = anchor1;
        this.anchor2 = anchor2;
        this.controlPoint = controlPoint;
    }

    public Point2D calculate(double t) {
        // (x, y) -> (x(t), p(t))
        int x = (int) FunctionEvaluator.evaluate(straightBetweenAnchorsX(), "t", t);
        int y = (int) FunctionEvaluator.evaluate(getFunction(), "t", t);
       
        return new Point2D(x, y);
    }

    private String getFunction() {
        return calculateGraphBetweennPoints().replaceAll("x", "("+straightBetweenAnchorsX() + ")");
    }

    /**
     * @return the graph of the BezierCurve in form of g(x) = ax^2 + bx + c as
     *         String
     */
    private String calculateGraphBetweennPoints() {
        // p(x) = a * x^2 + b * x + c
        // p'(x) = 2 * a * x + b

        // I  p'(anchor1.x) =
        double mA1C = (double) (controlPoint.y - anchor1.y) / (double) (controlPoint.x - anchor1.x);
        // = 2ax + b = 2 * anchor1.x * a + b

        // II p'(anchor2.x) =
        double mA2C = (double) (anchor2.y - controlPoint.y) / (double) (anchor2.x - controlPoint.x);
        // = 2ax + b = 2 * anchor2.x * a + b
       
        // I - II -> p'(anchor1.x) - p'(anchor2.x) = (2 * anchor1.x * a + b) - (2 *
        // anchor2.x * a + b)
        double a = (mA1C - mA2C) / (double) ((2 * anchor1.x) - (2 * anchor2.x));

        // a in I -> b
        double b = mA1C - (2 * a * (double) anchor1.x);

        // III p(anchor1.x) = anchor1.y
        double pA1x = (double) anchor1.y;
        // = ax^2 + b*x + c = anchor1.x^2 * a + anchor.x * b + c
       
        double c = pA1x - (a * Math.pow(anchor1.x, 2)) - (b * anchor1.x);

        return "(" + FunctionEvaluator.doubleToString(a) + ") * (x^2) + (" + FunctionEvaluator.doubleToString(b)
                + ") * x + (" + FunctionEvaluator.doubleToString(c) + ")";
    }

    public void setControlPoint(Point2D controlPoint) {
        this.controlPoint = controlPoint;
    }

    /**
     * @return a function for scaling the BezierCurve dependent on a parameter t =
     *         0, ... , 1 as String
     */
    private String straightBetweenAnchorsX() {
        return FunctionEvaluator.doubleToString((Math.abs(anchor1.x) + Math.abs(anchor2.x))) + " * t + ("
                + FunctionEvaluator.doubleToString(Math.min(anchor1.x, anchor2.x)) + ")";
    }
}

Während ich jetzt meine drei Punkte nutze, um eine Linie vom Typ Path2D.Double zu zeichnen, erstelle ich mit den gleichen Punkten ein Objekt vom Typ BezierCurve. Allerdings komm ich nicht so recht weiter, wie ich nun mit gegebener Koordinate eines Mausklicks bestimmen kann, ob dieser auf der Linie ist oder nicht.
 

CptK

Bekanntes Mitglied
Also, ich habe jetzt eine Bezierkurve 2. Grades implementiert, wobei ich nach folgendem Schema vorgegangen bin:
Anhang anzeigen 15127
Im folgenden Code werden die Schritte 1 - 4 durch diese Methoden umgesetzt:
Java:
1 -> calculateGraphBetweennPoints()
2 -> straightBetweenAnchorsX()
3 -> getFunction()
4 -> calculate()

Java:
public class BezierCurve {

    private Point2D anchor1;
    private Point2D anchor2;
    private Point2D controlPoint;

    public BezierCurve(Point2D anchor1, Point2D anchor2, Point2D controlPoint) {
        this.anchor1 = anchor1;
        this.anchor2 = anchor2;
        this.controlPoint = controlPoint;
    }

    public Point2D calculate(double t) {
        // (x, y) -> (x(t), p(t))
        int x = (int) FunctionEvaluator.evaluate(straightBetweenAnchorsX(), "t", t);
        int y = (int) FunctionEvaluator.evaluate(getFunction(), "t", t);
      
        return new Point2D(x, y);
    }

    private String getFunction() {
        return calculateGraphBetweennPoints().replaceAll("x", "("+straightBetweenAnchorsX() + ")");
    }

    /**
     * @return the graph of the BezierCurve in form of g(x) = ax^2 + bx + c as
     *         String
     */
    private String calculateGraphBetweennPoints() {
        // p(x) = a * x^2 + b * x + c
        // p'(x) = 2 * a * x + b

        // I  p'(anchor1.x) =
        double mA1C = (double) (controlPoint.y - anchor1.y) / (double) (controlPoint.x - anchor1.x);
        // = 2ax + b = 2 * anchor1.x * a + b

        // II p'(anchor2.x) =
        double mA2C = (double) (anchor2.y - controlPoint.y) / (double) (anchor2.x - controlPoint.x);
        // = 2ax + b = 2 * anchor2.x * a + b
      
        // I - II -> p'(anchor1.x) - p'(anchor2.x) = (2 * anchor1.x * a + b) - (2 *
        // anchor2.x * a + b)
        double a = (mA1C - mA2C) / (double) ((2 * anchor1.x) - (2 * anchor2.x));

        // a in I -> b
        double b = mA1C - (2 * a * (double) anchor1.x);

        // III p(anchor1.x) = anchor1.y
        double pA1x = (double) anchor1.y;
        // = ax^2 + b*x + c = anchor1.x^2 * a + anchor.x * b + c
      
        double c = pA1x - (a * Math.pow(anchor1.x, 2)) - (b * anchor1.x);

        return "(" + FunctionEvaluator.doubleToString(a) + ") * (x^2) + (" + FunctionEvaluator.doubleToString(b)
                + ") * x + (" + FunctionEvaluator.doubleToString(c) + ")";
    }

    public void setControlPoint(Point2D controlPoint) {
        this.controlPoint = controlPoint;
    }

    /**
     * @return a function for scaling the BezierCurve dependent on a parameter t =
     *         0, ... , 1 as String
     */
    private String straightBetweenAnchorsX() {
        return FunctionEvaluator.doubleToString((Math.abs(anchor1.x) + Math.abs(anchor2.x))) + " * t + ("
                + FunctionEvaluator.doubleToString(Math.min(anchor1.x, anchor2.x)) + ")";
    }
}

Während ich jetzt meine drei Punkte nutze, um eine Linie vom Typ Path2D.Double zu zeichnen, erstelle ich mit den gleichen Punkten ein Objekt vom Typ BezierCurve. Allerdings komm ich nicht so recht weiter, wie ich nun mit gegebener Koordinate eines Mausklicks bestimmen kann, ob dieser auf der Linie ist oder nicht.
Also kleines Update: der Code von oben ist Müll, funktioniert nicht und ist viel zu kompliziert, hier was besseres:
Java:
public static VectorPoint quadraticBezier(Point2D p0, Point2D p1, Point2D c, double t, style s, Color color) {
        double x = Math.pow(1 - t, 2) * p0.x + (1 - t) * 2 * t * p1.x + t * t * c.x;
        double y = Math.pow(1 - t, 2) * p0.y + (1 - t) * 2 * t * p1.y + t * t * c.y;
        return new VectorPoint(s, color, x, y);
    }
 

CptK

Bekanntes Mitglied
Falls jemand anderes ähnlich Funktionalitäten sucht wie ich und das nicht selbst schreiben möchte hier die ganze QuadraticBezier-Klasse:
Java:
package math.linear_algebra;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;

import math.Point2D;
import math.Point2D.style;

/**
 *
 * @author Marcus Kornmann
 * @version 1.0.0
 *
 */
public class QuadraticBezier {

    public final Point2D anchor1;
    public final Point2D anchor2;
    protected Point2D c1;

    public QuadraticBezier(Point2D anchor1, Point2D anchor2, Point2D controlPoint1) {
        this.anchor1 = anchor1;
        this.anchor2 = anchor2;
        this.c1 = controlPoint1;
    }

    /**
     *
     * @param t     Parameter depending on which the point is calculated
     * @param s     Style of calculated VectorPoint
     * @param color Color of calculated VectoPoint
     * @return a VectorPoint calculated depending on t
     */
    public VectorPoint calculate(double t, style s, Color color) {
        return quadraticBezier(anchor1, c1, anchor2, t, s, color);
    }

    /**
     * Computes a line of type Path2D.Double through the two anchor-points and the
     * control-point and draws it
     *
     * @param g2 Graphics
     */
    public void drawAsPath2DDouble(Graphics2D g2) {
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Path2D.Double line = new Path2D.Double();
        line.moveTo(anchor1.x, anchor1.y);
        line.curveTo(anchor1.x, anchor1.y, c1.x, c1.y, anchor2.x, anchor2.y);
        line.moveTo(anchor2.x, anchor2.y);
        line.closePath();
        g2.draw(line);
    }

    /**
     * Computes points in the interval step and draws a line between the current and
     * the next point
     *
     * @param g2    Graphics
     * @param step  distance between the calculated points
     * @param color Color of the line and the calculated VectorPoints
     */
    public void drawThroughPoints(Graphics2D g2, double step, Color color) {
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(color);
        VectorPoint p = calculate(0, style.dot, color);
        for (double i = 0.0; i <= 1; i = i + step) {
            VectorPoint q = calculate(i, style.dot, color);
            g2.drawLine((int) p.getData()[0], (int) p.getData()[1], (int) q.getData()[0], (int) q.getData()[1]);
            p = q;
        }
    }

    /**
     * Checks whether the Bézier-Line is clicked
     *
     * @param x        coordinate of the mouse click
     * @param y        coordinate of the mouse click
     * @param accuracy maximum allowed distance between line and click-position
     * @return return true, if there is a point on the line whose distance to the
     *         click-position is <= the give accuracy, false else
     */
    public boolean isClicked(int x, int y, double accuracy) {
        for (double i = 0.0; i <= 1; i = i + 0.001) {
            VectorPoint p = calculate(i, null, null);
            if (Math.sqrt(Math.pow(p.getData()[0] - x, 2) + Math.pow(p.getData()[1] - y, 2)) <= accuracy)
                return true;
        }
        return false;
    }

    /**
     * @param p0    First anchor-point
     * @param c     Control-point
     * @param p1    Second anchor-point
     * @param t     Parameter the point is calculated for
     * @param s     Style of calculated point
     * @param color Color of calculated point
     * @return the point on the line between p0, c, p1 dependent on t
     */
    public static VectorPoint quadraticBezier(Point2D p0, Point2D c, Point2D p1, double t, style s, Color color) {
        double x = Math.pow(1 - t, 2) * p0.x + (1 - t) * 2 * t * c.x + t * t * p1.x;
        double y = Math.pow(1 - t, 2) * p0.y + (1 - t) * 2 * t * c.y + t * t * p1.y;
        return new VectorPoint(s, color, x, y);
    }

}

Die isClicked-Methode ist vermutlich nicht optimal (was Effizienz angeht) umgesetzt aber sie funktioniert einwandfrei.
Die drawThroughPoints-Methode zeichnet eine Kurve die allerdings etwas verpixelt ist, falls jemand ne Methode hat, wie das besser geht, nur her damit.
Die drawAsPath2DDouble-Methode zwichnet zwar eine smoothe Kurve, dafür unterscheidet sich die Kurve leicht von der aus der eigenen Berechnung, keine Ahnung wieso.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
A Explicit casting Point2D.Double AWT, Swing, JavaFX & SWT 3
CptK Wie funktioniert contains() für Path2D.Double AWT, Swing, JavaFX & SWT 10
B AWT GUI nimmt nur 19 Ziffern an, obwohl Max Wert von Double größer ist AWT, Swing, JavaFX & SWT 7
T Swing Double Click bei Buttons in JTable AWT, Swing, JavaFX & SWT 9
L 2D-Grafik Frage zu Ellipse2D.Double, Abfrage, ob Punkt enthalten ist funktioniert nicht AWT, Swing, JavaFX & SWT 3
S Polygon mit double-Koordinaten AWT, Swing, JavaFX & SWT 2
N Double Buffern von BILDERN AWT, Swing, JavaFX & SWT 6
V Applet Double Buffering AWT, Swing, JavaFX & SWT 2
B Graphics2d drawString mit double Werten? AWT, Swing, JavaFX & SWT 5
O 2D-Grafik Rectangle mit double Werten zeichnen AWT, Swing, JavaFX & SWT 3
T 2D-Grafik Double Buffering AWT, Swing, JavaFX & SWT 7
lumo SWT Double Buffering AWT, Swing, JavaFX & SWT 5
M Object[][] in double[][] casten AWT, Swing, JavaFX & SWT 6
A aus einem Textfeld in double parsen AWT, Swing, JavaFX & SWT 10
K Flackern trotz Double-Buffering AWT, Swing, JavaFX & SWT 10
J JSlider mit Double Werten AWT, Swing, JavaFX & SWT 5
T JTextField nur Double werte eingeben AWT, Swing, JavaFX & SWT 2
A TableRowSorter für double Werte mit Formatierung AWT, Swing, JavaFX & SWT 3
iLoveTheInternet AWT Rectangle2D.Double intersecs AWT, Swing, JavaFX & SWT 6
hdi Wie implementiere ich Double Buffering AWT, Swing, JavaFX & SWT 12
R drawString mit double Koordinaten AWT, Swing, JavaFX & SWT 5
L JTextField Umwandeln in double-Werte AWT, Swing, JavaFX & SWT 5
K double / int einlesen? AWT, Swing, JavaFX & SWT 12
G Frage zu Double Buffering AWT, Swing, JavaFX & SWT 4
L double in JTable formatieren AWT, Swing, JavaFX & SWT 7
P Integer, Double, etc mit getTableCellRendererComponent AWT, Swing, JavaFX & SWT 6
P Double-Werte sortieren mit TableRowSorter AWT, Swing, JavaFX & SWT 2
U Double Buffering AWT, Swing, JavaFX & SWT 6
B JFormattedTextField mit DecimalFormat in ein Double AWT, Swing, JavaFX & SWT 11
J Double-Buffering zu langsam AWT, Swing, JavaFX & SWT 4
S double in String umwandeln AWT, Swing, JavaFX & SWT 6
L Double-Buffering AWT, Swing, JavaFX & SWT 2
R Double in der Tabelle statt String und Integer AWT, Swing, JavaFX & SWT 3
Student "Dicke" einer Linie - Line2D.Double AWT, Swing, JavaFX & SWT 12
G Double-Click f. alle Komps UNIX u. WIN verschieden ? AWT, Swing, JavaFX & SWT 5
D double Buffering abschalten? AWT, Swing, JavaFX & SWT 4
G Ellipse2D.Double funktioniert nicht AWT, Swing, JavaFX & SWT 3
W SWT - Tree: Problem mit Double-Click expand AWT, Swing, JavaFX & SWT 5
L Double Buffering bei JTable AWT, Swing, JavaFX & SWT 4
J Java2D Kreis/Kurven Frage AWT, Swing, JavaFX & SWT 2

Ähnliche Java Themen

Neue Themen


Oben