0

OBS! Changed as part of the question has been answered.

My math has been fixed due to your help and input, the same with StackOverflowError but I still can get my head around how to make the circle move from one x,y point to another. Currently I just repeat the drawing multiple places.

public class MyFrame extends JPanel {
        int xc = 300, yc = 300, r = 100, diam = 50;
        double inc = Math.PI / 360, theta = 0;

        public void paintComponent(Graphics g) {

                Timer timer = new Timer(0, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                                theta = theta + inc;
                                repaint();
                        }
                });
                timer.setDelay(2);
                timer.start();
        }
        @Override
        public void paint(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); //smooth the border around the circle
                g2d.rotate(theta, xc, yc);
                g2d.setColor(Color.blue);
                g2d.drawOval(xc + r - diam / 2, yc + r - diam / 2, diam, diam);
paintComponent(g);
        }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Narj
  • 39
  • 5
  • You have to use radians for cos and sin functions. – Jhanzaib Humayun May 13 '22 at 20:18
  • @user16320675 I forgot to mention the StackoverflowError, you are right on this, by deleting this and changing to Math.sin(Math.toRadians((i*360/n))) (and cos)! Now I just need to figure out how to move the circle between the points instead of drawing 8 different ones, thank you. – Narj May 13 '22 at 21:28

2 Answers2

0

Math.sin and Math.cos methods expect a value in radians. You can convert degrees to radians by multiplying with Math.PI/180

Therefore, try changing Math.cos(i * 360 / n) and Math.sin(i * 360 / n) to Math.cos((i * 360 / n)*(Math.PI/180)) and Math.sin((i * 360 / n)*(Math.PI/180)).

MWiesner
  • 8,868
  • 11
  • 36
  • 70
Jhanzaib Humayun
  • 1,193
  • 1
  • 4
  • 10
  • Thank you for your input @Jhanzaib, unfortunately it didn't work, neither did it changing it to Math.sin(Math.toRadians((i*360/n))); – Narj May 13 '22 at 20:32
0

This should help you get started. You can modify it as you see fit. It simply has an outer circle revolve around an inner red dot at the center of the panel.

  • First, rotate the graphics context, and not the circle location around the center. Thus, no trig is required.
  • Anti-aliasing simply fools the eye into thinking the graphics are smoother.
  • BasicStroke sets the thickness of the line
  • you need to put the panel in a frame.
  • and super.paintComponent(g) should be first statement in paintComponent to clear panel (and do other things).
  • the timer updates the angle by increment and invokes repaint. A larger increment will make a quicker but more "jerky" motion about the center. If you set the angle to Math.PI/4, then you need to increase the timer delay (try about 1000ms).
  • Check out the Java Tutorials for more on painting.
  • Anything else I omitted or forgot should be documented in the JavaDocs.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

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

public class DrawCircle extends JPanel {
    int width = 500, height = 500;
    final int xc = width/2, yc = height/2;
    int r = 100; // radius from center of panel to center of outer circle
    int diam = 50; // outer circle diamter
    double inc = Math.PI/360; 
    double theta = 0;
    JFrame f = new JFrame();

    public static void main(String[] args) {
        SwingUtilities.invokeLater(()-> new DrawCircle().start());
    }

    public void start() {
        f.add(this);
        f.pack();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        Timer timer = new Timer(0, (ae)-> { theta += inc; repaint();});
        timer.setDelay(20);
        timer.start();
    }
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        g2d.rotate(theta, xc, yc);
        g2d.setColor(Color.red);
        g2d.fillOval(xc-3, yc-3, 6, 6); // center of panel
        g2d.setStroke(new BasicStroke(3));
        g2d.setColor(Color.blue);
//      g2d.drawLine(xc,yc, xc+r, yc); // tether between centers 
        g2d.drawOval(xc+r-diam/2, yc-diam/2, diam,diam);
    }

}

Updated Answer

OK, there are two fundamental things you are doing wrong.

  • You are not adding super.paintComponent(g) as the first statement in your paintComponent method.
  • you are overridding paint(Graphics g) (whether you intend to or not) because it is also public and is inherited by JPanel from JComponent. Do not use paint() as it isn't necessary here (maybe in some applications but I have never had the need). Move all the code in there to paintComponent

You should also move the timer code outside of paintComponent. It only needs to be defined once and is run in the background. It will continue to call your ActionListener class until you stop it.

Now, after doing the above you might ask "why is only one circle showing up when I draw?" The obvious answer is that I only wanted to draw one. But why doesn't it get repeated each time?

Because super.paintComponent(g) clears the JPanel each time paintComponent is invoked just as it is supposed to. So if you want to draw multiple circles (or other things), you need to put them in a list and draw them from within paintComponent. Since all events including painting and your timer are run in series on a single thread (the Event Dispatch Thread) it is important to keep processing to a minimum. So when possible, most calculations should be done outside of that thread. EDT processing should be as simple and as quick as possible.

My first answer showed a circle orbiting a point. But perhaps that is not what you want. You may just want to position circles uniformly around the center from a fixed distance. I have provided two methods of doing that.

  • Using rotate as before. Imo, it is the simplest. The angle is fixed and each time rotate is called, it is additive. So just call that method nCircle times and draw the circle. And remember to compute the x and y coordinates to correct for the radius.
  • Using trig to calculate the location of the circles. This uses a list of angles based on the nCircles. For each iteration, x and y are computed based on the radius and current angle.

Both of these are shown in different colors to demonstrate their overlay.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;

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

public class DrawCircle2 extends JPanel {
    int width = 500, height = 500;
    final int xc = width / 2, yc = height / 2;
    int r = 100; // radius from center of panel to center of outer circle
    int diam = 50; // outer circle diamter
    int nCircles = 8; // number of circles
    
    double theta = Math.PI*2/nCircles;
    
    List<Point> coords1 = fillForTrig();
    JFrame f = new JFrame();
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new DrawCircle2().start());
    }
    
    private List<Point> fillForTrig() {
        List<Point> list = new ArrayList<>();
        for (int i = 0; i < nCircles; i++) {
            int x = xc+(int)(r*Math.sin(i*theta));
            int y = yc+(int)(r*Math.cos(i*theta));
            list.add(new Point(x,y));
        }
        return list;
    }
    
        
    public void start() {
        
        f.add(this);
        f.pack();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    
    public Dimension getPreferredSize() {
        return new Dimension(width, height);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        drawUsingRotate(g2d);
//      drawUsingTrig(g2d);
    }

    private void drawUsingRotate(Graphics2D g2d) {
        g2d = (Graphics2D)g2d.create();
        g2d.setColor(Color.RED);
        //fixed positions for radius as context is rotated
        int xx =  0;
        int yy  = r;
        for (int i = 0; i < nCircles;i++) {
             g2d.rotate(theta, xc, yc);
             // xx-diam/2 just places the center of the orbiting circle at
             // the proper radius from the center of the panel. Same for yy.
             g2d.drawOval(xc + xx - diam / 2, yc + yy - diam / 2, diam,
                        diam);
            
         }
         g2d.dispose();
    }
    private void drawUsingTrig(Graphics2D g2d) {
        g2d = (Graphics2D)g2d.create();
        g2d.setColor(Color.BLUE);
        for (Point p : coords1) {
            int x = (int)p.getX();
            int y = (int)p.getY();
            g2d.drawOval(x-diam/2, y-diam/2, diam, diam);
        }
        g2d.dispose();
    }
}
WJS
  • 36,363
  • 4
  • 24
  • 39
  • TY, but that is way to complicated for me. I've seen similar earlier today, and tried to figure them out regarding to my own example and usecase, but I couldn't make it work for me. A bit above my level at this point, especially with shortcuts like (ae) and anonymous classes. – Narj May 13 '22 at 23:40
  • Well, you're talking about using trig to compute the orbital path of the outer circle. But most of what I did you should be doing too to properly paint. – WJS May 13 '22 at 23:47
  • TY for your comment, you are probably right, but as you might see in my updated code it seems to be above my level, as it still wont work. – Narj May 14 '22 at 00:16
  • WJS - I've now added to call the paintComponent() method, but now I got myself stuck in a loop where none of my other methods will ever be called. - My circle is also moving extremely fast at the beginning and then slowing down to a steady phase - why is that, as I use the same parameters as you? – Narj May 14 '22 at 12:08
  • I provided additional information in my previous answer. Probably that you keep defining a new timer every time `paintComponent` is called. Move it outside of that method. – WJS May 14 '22 at 18:44