# Autos auf kurviger Strasse...



## mroz (29. Apr 2010)

Guten Tag
Habe folgendes problem: ich simuliere eine strasse mit autos von links
nach rechts... Nun möchte ich eine 180. Grad kurve einbauen. Wie gehe
ich das am besten an? Im moment versuche ich eine funktion f(x) die
mir das entsprechende y zurück gibt da das x ja immer konstant erhöht
wird...leider ohne erfolg... Hatt mir jemand einen tipp?

Vielen dank


----------



## Gast2 (29. Apr 2010)

sin() und cos() ??


----------



## Landei (29. Apr 2010)

Eine 180°-Kurve lässt sich mit einer Kurve y = f(x) nicht abbilden, denn nach dem "Wenden" würde das Auto in Gegenrichtung fahrten, also würde es zu einem x-Wert zwei y-Werte geben. Eine Möglichkeit der Darstellung wäre eine parametrisierte Kurve, z.B. wenn sich die Koordinaten aus der Zeit t ergeben, also x = f(t), y = g(t). Damit sind beliebige Kurven möglich, auch 180°-Kurven oder ein selbstüberkreuzender Weg.


----------



## Steev (29. Apr 2010)

Pfadanimationen?

SourceForge.net Repository - [rpgenesis] Index of /trunk/src/RPGenesis/com/rpgenesis/animation/path


----------



## mroz (1. Mai 2010)

vielen dank für die lösungsvorschäge!


Landei hat gesagt.:


> Eine 180°-Kurve lässt sich mit einer Kurve y = f(x) nicht abbilden, denn nach dem "Wenden" würde das Auto in Gegenrichtung fahrten, also würde es zu einem x-Wert zwei y-Werte geben. Eine Möglichkeit der Darstellung wäre eine parametrisierte Kurve, z.B. wenn sich die Koordinaten aus der Zeit t ergeben, also x = f(t), y = g(t). Damit sind beliebige Kurven möglich, auch 180°-Kurven oder ein selbstüberkreuzender Weg.





diesen Vorschlag ist am besten für mich umzusetzen da es ja in der tat Zeitabhängig ist...

hab es mit x = x + sin(winkel)*CONST probiert (wobei ich den winkel ein wenig erhöhe bis sin(winkel) = 1 ist) leider komme ich mit dieser variante nicht weiter , das entsprechende hab ich natürlich für y gemacht...wie kann ich das am besten lösen?  

DANKE!


----------



## Marco13 (1. Mai 2010)

Ich glaube, du musst genauer beschreiben, was du vorhast:

1. Soll das Auto über einen vorgegebenen Pfad fahren?
Wenn ja, kannst du das machen, was Landei und Steev angedeutet haben. Man könnte entweder selbst von Hand einen GeneralPath entlanglaufen, oder vorgefertigte Klassen dafür verwenden, wie die von Steev verlinkte, oder das https://timingframework.dev.java.net/ oder so..

2. Soll man das Auto beliebig "rumfahren" können (auf einem Pfad, der NICHT vorher bekannt ist)?
Dann würde man wohl eher den Zustand des Autos speichern (Position, Bewegungsrichtung, Lengungseinschlag) und den dann kontinuierlich aktualisieren, während das Auto fährt


----------



## ice-breaker (1. Mai 2010)

mroz hat gesagt.:


> hab es mit x = x + sin(winkel)*CONST probiert (wobei ich den winkel ein wenig erhöhe bis sin(winkel) = 1 ist) leider komme ich mit dieser variante nicht weiter , das entsprechende hab ich natürlich für y gemacht...wie kann ich das am besten lösen?


Bezier Kurven?


----------



## mroz (3. Mai 2010)

Marco13 hat gesagt.:


> Ich glaube, du musst genauer beschreiben, was du vorhast:
> 
> 1. Soll das Auto über einen vorgegebenen Pfad fahren?
> Wenn ja, kannst du das machen, was Landei und Steev angedeutet haben. Man könnte entweder selbst von Hand einen GeneralPath entlanglaufen, oder vorgefertigte Klassen dafür verwenden, wie die von Steev verlinkte, oder das https://timingframework.dev.java.net/ oder so..
> ...




Das auto sollte einen vorgegebenen pfad fahren...das beste wär wenn ich einem GeneralPath folgen könnte...jedoch wenn eine kurve kommt habe ich probleme mit dem berechnen da ich ja nur vier koordinaten kenne von der kurve...


----------



## Steev (3. Mai 2010)

Man könnte die Kurven (Beziér-Kurven) auch einfach rekursiv in Geraden zerlgen (Siehe Link, weiter oben). Dann bräuchte man das gewünschte Objekt nur noch relativ auf den jeweils aktuellen Knotenpunkt zubewegen.


----------



## Marco13 (3. Mai 2010)

Da hatte ich hier noch was in der Schublade liegen. Das hat eigentlich die Vecmath verwendet, aber die paar Funktionen hab ich jetzt mal schnell nachgebaut. Ist im Prinzip nur eine (nicht besonders "geschickte" aber doch funktionierende) implementierung vom DeCasteljau. Wenn man dort die 4 Punkte aus einen GeneralPath reinsteckt kriegt man nach einem "update" die Zwischenpunkte raus - und praktischerweise auch gleich die Tangenten, was du für die richtige Ausrichtung des Autos brauchen wirst...

Das ist jetzt leider nicht kommentiert oder so... das war (noch) nicht zur Veröffentlichung gedacht... aber ... ist hiermit in den public domain entlassen - unter der WTFPL-Lizenz 


```
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import java.util.List;

public class BezierSample extends JFrame
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new BezierSample().setVisible(true);
            }
        });
    }


    public BezierSample()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500,500);

        getContentPane().setLayout(new BorderLayout());

        BezierCanvas bezierCanvas = new BezierCanvas();
        getContentPane().add(bezierCanvas, BorderLayout.CENTER);

        BezierCurve bezierCurve0 = new BezierCurve();
        bezierCurve0.addControlPoint(new Point3f(100,100,0));
        bezierCurve0.addControlPoint(new Point3f(100,200,0));
        bezierCurve0.addControlPoint(new Point3f(200,200,0));
        bezierCurve0.addControlPoint(new Point3f(200,100,0));
        bezierCanvas.addBezierCurve(bezierCurve0);
    }
}



class BezierCurve
{
    private int numControlPoints;
    private Point3f controlPoints[];

    private List<Point3f> points;

    private List<Vector3f> tangents;

    private int numPoints = 20;

    private Point3f pointBuffer[] = null;

    public BezierCurve()
    {
        points = new ArrayList<Point3f>();
        tangents = new ArrayList<Vector3f>();
    }

    public int getNumPoints()
    {
        return points.size();
    }

    public Point3f getPointRef(int index)
    {
        return points.get(index);
    }

    public Vector3f getTangentRef(int index)
    {
        return tangents.get(index);
    }

    public void addControlPoint(Point3f controlPoint)
    {
        if (controlPoints == null || controlPoints.length == numControlPoints)
        {
            Point3f newControlPoints[] = new Point3f[numControlPoints+1];
            if (controlPoints != null)
            {
                System.arraycopy(controlPoints, 0, newControlPoints, 0, numControlPoints);
            }
            controlPoints = newControlPoints;
        }
        controlPoints[numControlPoints] = controlPoint;
        numControlPoints++;
        update();
    }

    public int getNumControlPoints()
    {
        return numControlPoints;
    }

    public Point3f getControlPointRef(int index)
    {
        return controlPoints[index];
    }


    private void casteljau(Point3f result, Vector3f tangent, Point3f b[], int n, float t)
    {
        casteljauR(result, tangent, b, n, t);
    }

    private void casteljauR(Point3f result, Vector3f tangent, Point3f b[], int n, float t)
    {
        if (pointBuffer == null || pointBuffer.length < 2 * (n+1))
        {
            pointBuffer = new Point3f[2*(n+1)];
            for (int i=0; i<pointBuffer.length; i++)
            {
                pointBuffer[i] = new Point3f();
            }
        }
        casteljauRec(result, tangent, b, 0, n, t);
    }

    void casteljauRec(Point3f result, Vector3f tangent, Point3f b[], int i, int n, float t)
    {
        if (n==0)
        {
            result.set(b[i]);
            return;
        }

        casteljauRec(pointBuffer[2*n+0], null, b, i+1, n-1, t);
        casteljauRec(pointBuffer[2*n+1], null, b, i  , n-1, t);

        if (result != null)
        {
            result.interpolate(pointBuffer[2*n+1],pointBuffer[2*n+0],t);
        }

        if (tangent != null)
        {
            tangent.sub(pointBuffer[2*n+1],pointBuffer[2*n+0]);
            tangent.scale(n);
        }
    }


    public void update()
    {
        points.clear();
        tangents.clear();
        for (int i=0; i<numPoints; i++)
        {
            points.add(new Point3f());
            tangents.add(new Vector3f());
        }
        for (int i=0; i<numPoints; i++)
        {
            casteljau(points.get(i), tangents.get(i), controlPoints, numControlPoints-1, i/(numPoints-1.0f));
        }
    }

}



class BezierCanvas extends JComponent implements MouseListener, MouseMotionListener
{
    private ArrayList<BezierCurve> bezierCurves = new ArrayList<BezierCurve>();

    private Point3f draggedPoint = null;

    public BezierCanvas()
    {
        addMouseListener(this);
        addMouseMotionListener(this);
    }


    public void addBezierCurve(BezierCurve bezierCurve)
    {
        bezierCurves.add(bezierCurve);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        for (int i=0; i<bezierCurves.size(); i++)
        {
            paint(g, bezierCurves.get(i));
        }
    }

    private void paint(Graphics g, BezierCurve bezierCurve)
    {
        g.setColor(Color.RED);
        for (int i=0; i<bezierCurve.getNumControlPoints(); i++)
        {
            Point3f p0 = bezierCurve.getControlPointRef(i);
            int x0 = (int)p0.x;
            int y0 = (int)p0.y;
            g.drawOval(x0-4,y0-4,8,8);
        }

        g.setColor(Color.BLACK);
        for (int i=0; i<bezierCurve.getNumPoints()-1; i++)
        {
            Point3f p0 = bezierCurve.getPointRef(i);
            Point3f p1 = bezierCurve.getPointRef(i+1);

            //System.out.println(p0);

            int x0 = (int)p0.x;
            int y0 = (int)p0.y;
            int x1 = (int)p1.x;
            int y1 = (int)p1.y;

            g.drawLine(x0,y0,x1,y1);
            g.drawLine(x0-2,y0-2,x0+2,y0+2);
            g.drawLine(x0-2,y0+2,x0+2,y0-2);
        }


        g.setColor(Color.GRAY);
        for (int i=0; i<bezierCurve.getNumPoints(); i++)
        {
            Point3f p0 = bezierCurve.getPointRef(i);
            Vector3f t0 = bezierCurve.getTangentRef(i);

            int x0 = (int)p0.x;
            int y0 = (int)p0.y;

            int x1 = (int)(p0.x + t0.x);
            int y1 = (int)(p0.y + t0.y);

            g.drawLine(x0,y0,x1,y1);
        }


    }


    public void mousePressed(MouseEvent e)
    {
        for (int i=0; i<bezierCurves.size(); i++)
        {
            BezierCurve bezierCurve = bezierCurves.get(i);

            for (int j=0; j<bezierCurve.getNumControlPoints(); j++)
            {
                Point3f p0 = bezierCurve.getControlPointRef(j);
                int x0 = (int)p0.x;
                int y0 = (int)p0.y;
                Point p = new Point(x0,y0);
                float dist = (float)e.getPoint().distance(p);
                if (dist < 4)
                {
                    draggedPoint = p0;
                    return;
                }
            }
        }

    }
    public void mouseReleased(MouseEvent e)
    {
        draggedPoint = null;
    }
    public void mouseClicked(MouseEvent e)
    {
    }
    public void mouseEntered(MouseEvent e)
    {
    }
    public void mouseExited(MouseEvent e)
    {
    }
    public void mouseMoved(MouseEvent e)
    {
    }
    public void mouseDragged(MouseEvent e)
    {
        if (draggedPoint == null)
        {
            return;
        }
        draggedPoint.x = e.getX();
        draggedPoint.y = e.getY();

        for (int i=0; i<bezierCurves.size(); i++)
        {
            BezierCurve bezierCurve = bezierCurves.get(i);
            bezierCurve.update();
        }
        repaint();

    }


}




class Tuple3f
{
    float x;
    float y;
    float z;

    public void interpolate(Tuple3f t0, Tuple3f t1, float alpha)
    {
        float dx = t1.x-t0.x;
        float dy = t1.y-t0.y;
        float dz = t1.z-t0.z;
        x = t0.x + dx * alpha;
        y = t0.y + dy * alpha;
        z = t0.z + dz * alpha;
    }

    public void set(Tuple3f that)
    {
        this.x = that.x;
        this.y = that.y;
        this.z = that.z;
    }

    public void sub(Tuple3f t0, Tuple3f t1)
    {
        this.x = t0.x-t1.x;
        this.y = t0.y-t1.y;
        this.z = t0.z-t1.z;
    }

    public void scale(float factor)
    {
        this.x *= factor;
        this.y *= factor;
        this.z *= factor;
    }

    public void subX(Tuple3f that)
    {
        this.x -= that.x;
        this.y -= that.y;
        this.z -= that.z;
    }

}

class Point3f extends Tuple3f
{
    public Point3f()
    {
    }
    public Point3f(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

}

class Vector3f extends Tuple3f
{
}
```


----------



## Marco13 (3. Mai 2010)

Ah OK, Steev's Link enthielt in der DefaultPath auch schon den DeCasteljau ... sorry


----------

