Kreis animiert zeichnen

Hallo,

ist es irgendwie möglich einen Kreis “nach und nach” (also wie ein Stift auf Papier) zu zeichnen?
Das einzige was ich bisher vollbracht habe sind vollständige Kreise die nach und nach größer werden, aber das ist nicht das was ich beabsichtige.

Google hat mir bisher auch nicht gesagt wie ich da anfangen könnte.

Danke

Naja theoretisch kannst du dann einfach jeden Punkt nach und nach selber zeichnen.
Obs dafür allgemein ne fertige Methode gibt, weiß ich net.

[java.awt.Graphics#drawArc](http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawArc(int, int, int, int, int, int))

Perfekt, genau was ich brauchte!

Kurzers KSKB falls das mal wieder jemand sucht:

import java.awt.Graphics;

public class ZeichneKreis extends Frame
{
	public static void main(String[] args)
	{
		new ZeichneKreis();
	}

	public ZeichneKreis()
	{
		setSize(200, 200);
		setVisible(true);
	}

	@Override
	public void paint(Graphics g)
	{
		int arcAngle = 2;
		int startAngle = 0;

		while (startAngle < 360)
		{
			g.drawArc(20, 40, 140, 140, startAngle, arcAngle);
			startAngle += arcAngle;
			try
			{
				Thread.sleep(50);
			}
			catch (InterruptedException e)
			{
				System.out.println(e);
			}
		}
	}
}```

Das würde ich lieber wieder rausnehmen.
Grundsätzliche Todsünde: innerhalb der paint Methode einer Komponenten sleep aufrufen.
Diese Methoden sollten, möglichst zügig abgearbeitet werden.
Ebenfalls nicht schön: die paint eines JFrames zu überschreiben…

Besser den Winkel in einem separaten Thread, Timer o.ä. Schritt für Schritt erhöhen und daraus einfach nur repaint() auf die Komponent aufrufen, dazu z.B. die paintComponent eines JPanels überschreiben.

######EDIT#######
sehe gerade Du verwendest AWT nicht Swing, warum auch immer??? Da gilt dann natürlich Panel mit überschriebener paint

Warum AWT und nicht SWING? Weil das 15 Jahre alte Dingsbums hier halt AWT macht.
Warum sleep() und kein Panel? Weil das schneller zu testen ging als n Timer und hier sowieso nur das drawArc eingebaut wird.

Und zum gucken wie der Kreis “gemalt” wird reicht das.

Dann sollte man einen Code mit solchen grundlegenden Fehlern aber nicht hier reinstellen.

Der Aufwand um es besser zu machen ist auch nicht allzu groß:

import java.awt.Graphics;
import java.awt.Panel;

public class ZeichneKreisDemo {
	public static void main(String[] s) {
		new ZeichneKreisDemo().startDemo();
	}
	
	private int arcAngle = 0;
	
	public void startDemo() {
		Frame frame = new Frame();
		frame.setSize(200, 200);
		final Panel panel = new Panel() {
			public void paint(Graphics g) {
				g.drawArc(20, 20, 140, 140, 0, arcAngle);
			}
		};
		frame.add(panel);
		frame.setVisible(true);

		new Thread(new Runnable() {
			public void run() {
				while (arcAngle < 360) {
					try {
						Thread.sleep(50);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					arcAngle += 2;
					panel.repaint();
				}
			}
		}).start();
	}
}```

interessanterweise flimmert der Kreis ein wenig, je nach Winkel
da könnte es besser sein, immer den vollen Kreis zu malen und einen Teil weiß zu übermalen

allerdings bisschen dicker weiß, sonst wieder Flimmern,
mit Clippen usw. geht es sicher noch besser

                    Graphics2D g2d = (Graphics2D)g;
                    g2d.drawArc(20, 20, 140, 140, 0, 360);

                    g2d.setColor(Color.WHITE);
                    g2d.setStroke(new BasicStroke(4));
                    g2d.drawArc(20, 20, 140, 140, arcAngle, 360 - arcAngle);

Ich habe mir auch eine kleine Demo geschrieben. Ein Flimmern des Kreises konnte ich nicht wahrnehmen. Größere Winkel, die in jedem Schritt hinzugefügt werden, lassen die Animation etwas unflüssig wirken, weil der Kreisbogen plötzlich ein ganzes Stück wächst.

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class DrawArcAnimationDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Arc Animation Demo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                Rectangle bounds = new Rectangle(50, 50, 150, 150);
                ArcData arcData = new ArcData(bounds, 90, 0, 2);
                ArcAnimationPanel panel = new ArcAnimationPanel(arcData, 50);
                panel.setPreferredSize(new Dimension(250, 250));
                panel.start();

                frame.add(BorderLayout.CENTER, panel);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

class ArcAnimationPanel extends JPanel {
    private final ActionListener animator = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            if (currentAngle >= 360) {
                currentAngle = 0;
                animationTimer.restart();
            } else {
                currentAngle += data.getAngleIncrementValue();
            }
            repaint();
        }
    };

    private final Timer animationTimer;

    private final ArcData data;
    private int currentAngle;

    public ArcAnimationPanel(ArcData data, int delay) {
        super();
        this.animationTimer = new Timer(delay, animator);
        this.data           = data;
        this.currentAngle   = data.getArcAngle();
    }

    public void start() {
        animationTimer.start();
    }

    public void stop() {
        animationTimer.stop();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int x = data.getBounds().x;
        int y = data.getBounds().y;
        int w = data.getBounds().width;
        int h = data.getBounds().height;
        int startAngle = data.getStartAngle();

        g.drawArc(x, y, w, h, startAngle, currentAngle);
    }
}

final class ArcData {
    private final Rectangle bounds;
    private final int startAngle, arcAngle;
    private final int angleIncrementValue;

    public ArcData(
            Rectangle bounds,
            int startAngle, int arcAngle,
            int angleIncrementValue) {
        this.bounds              = new Rectangle(bounds);
        this.startAngle          = startAngle;
        this.arcAngle            = arcAngle;
        this.angleIncrementValue = angleIncrementValue;
    }

    public Rectangle getBounds()              { return new Rectangle(bounds); }
    public int       getStartAngle()          { return startAngle; }
    public int       getArcAngle()            { return arcAngle; }
    public int       getAngleIncrementValue() { return angleIncrementValue; }
}

Liegt es vieleicht an AWT? Mit Swing würde ich keine Flimmern erwarten da double buffered. Soweit ich weiß muss man das mit AWT selbst implementieren… ?

Ja, das liegt am nicht vorhandenen DoubleBuffering in AWT. In Swing sind Komponenten bereits von Hause aus doppelt gepuffert, daher wird dort auch nichts flimmern oder flackern.
In AWT muss das, was als nächstes auf den Bildschirm gezeichnet werden soll, auf einem Offscreen-Image vorbereitet werden.
Im Wiki ist mindestens ein Beitrag darüber zu finden.

ich meinte die exakten Pixel die gemalt/ für alle Zeit (bis zum nächsten Malen) in Stein bzw. in den Bildschirm gemeißelt werden,
nix mit Double Buffering, sondern welche Zeichenpixel bestimmt werden,
minimale Abweichungen, je nach Größe des Winkels im letzten angefangenen Viertel

wenn man mein Programm nimmt und das Stroke wegnimmt, also mit Weiß exakt drüber malt, sieht man vielleicht die schwarzen Punkte,
oder bei anderen nicht so

Prinzipiell könnte man einen Kreis auch stückchenweise als einzelne Linien oder gar Punkte zeichnen.

Punkte (x,y) eines Kreises erhält man z.B. mit:
x = xm + r * cos ( t * pi )
y = ym + r * sin ( t * pi )
für -1 < t <= 1

mit
xm: X-Koordinate des Kreismittelpunktes
ym: Y-Koordinate des Kreismittelpunktes
r : Kreisradius
pi : Kreiszahl ( ca. 3.1415926535 )

hm, nette Idee, dann könnte ich mit den Koordinaten noch was anstellen… (Stift der malt oder so^^)

Mal für was anderes merken, für meinen Fall reicht die simpelste Implementation.

hm iwas hab ich da noch nicht ganz kapiert.

double ym = 100;
double t = 0;
double r = 50;
double x = xm + r * Math.cos(t * Math.PI);
double y = ym + r * Math.sin(t * Math.PI);
g.drawLine((int)x, (int)y, (int)x, (int)y);```
was soll denn das `t` darstellen?

und wo kommt die Variable zum Zuge die enthält wie weit der Kreis schon gezeichnet ist?

Das ist t.

den Gedanken hatte ich auch schon, aber was mich davon abgehalten hat war:

*** Edit ***

jetzt kommt das raus:

muss ich den Winkel vllt. umrechenen sodass er das da oben im Quote erfüllt?
wenn ja, …ähm wie?

t wird mit PI multipliziert, 2 PI ist der Bereich eines ganzen Kreises, von 0 bis 360 Grad,
Math.cos und Math.sin liefern die Koordinaten,


wobei der Artikel wenig anschauliches enthält…

soo hier nochmal das ganze:


import java.awt.EventQueue;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

public class Fesnter extends JFrame
{
	private JPanel contentPane;
	private int arcAngle = 0;

	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
		{
			@Override
			public void run()
			{
				try
				{
					Fesnter frame = new Fesnter();
					frame.setVisible(true);
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
			}
		});
	}

	public Fesnter()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 200, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new FormLayout(new ColumnSpec[] { ColumnSpec
				.decode("default:grow"), }, new RowSpec[] { RowSpec
				.decode("default:grow"), }));

		final JPanel panel = new JPanel()
		{
            @Override
			public void paint(Graphics g)
            {
            	double xm = 100;
            	double ym = 100;
            	double t = arcAngle * Math.PI / 180;
            	double r = 50;
            	double x = xm + r * Math.cos(t * Math.PI);
            	double y = ym + r * Math.sin(t * Math.PI);
            	g.drawLine((int)x, (int)y, (int)x, (int)y);
            	System.out.println(x + " | " + y + " | " + t + " | " + arcAngle);
            }
        };
		contentPane.add(panel, "1, 1, fill, fill");

		new Thread(new Runnable() {
            @Override
			public void run() {
                while (arcAngle < 360) {
                    try
                    {
                        Thread.sleep(50);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    arcAngle += 1;
                    panel.repaint();
                }
            }
        }).start();
	}
}```

das einzige was mich jetzt noch wundert ist, dass er mehrmals "im Kreis läuft", bevor der Kreis fertig ist.
Hab ich da noch was an meiner Umrechnung versemmelt?

Laut google Winkel -> Bogenmaß : `(Winkel in Bogenmaß) = (Winkel in Gradmaß) * pi / 180 Grad`

bei der Umrechtung von t multiplizierst du mit PI, bei der Verwendung kommt nochmal Faktor PI dazu,
t wächst in der Verwendung schnell,
bald zweiter und dritter Kreis, da zum Glück Verschiebung, so dass fehlende Punkte ergänzt werden

mit double t = arcAngle / 180.0; sieht es besser aus

ob die Anzahl der Zwischenschritte reichen hängt aber auch von der Darstellung, vom verwendeten Radius ab, bei 250 wieder deutliche Lücken…,
dann lieber Winkel in 0.1 Grad-Schritten usw., evtl. auch direkt t um 0.001 erhöhen usw.,

allgemeines Konzept zu überlegen, aus dem Radius kann man sicher ne Formel für passendes Schrittmaß finden,
oder naheliegend tatsächlich Linien malen, zwischen je zwei berechneten Punkten