I'm currently writing a little game and I came over a problem. I need to draw 64 small circles at the border of a big circle. So I want something like this:
How can this be done in java, using the java.awt.Component#paint() method and the java.awt.Graphics class?
Thanks.
CodePudding user response:
So, your basic problem comes down to "find a point on a circle based on a given angle"
A quick google will find resources like
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int midX = getWidth() / 2;
int midY = getHeight() / 2;
Dimension size = new Dimension(4, 4);
g2d.setColor(Color.BLACK);
for (int index = 0; index < 64; index ) {
double angle = (360d / 64d) * index;
Point2D poc = getPointOnCircle(angle, 100 - 4);
Rectangle2D box = new Rectangle2D.Double(midX poc.getX() - 2, midY poc.getY() - 2, size.width, size.height);
g2d.draw(box);
}
g2d.dispose();
}
protected Point2D getPointOnCircle(double degress, double radius) {
double rads = Math.toRadians(degress - 90); // 0 becomes the top
return new Point2D.Double(Math.cos(rads) * radius, Math.sin(rads) * radius);
}
}
}
So, about now, you should realise that my "squares" are, well, square, not "dimond" shaped like yours. This is where you're going to have to start doing some work.
If I was approaching this problem I might be tempted, to create a custom shape or, apply a 45 degree transformation to the box
and the translate it's position to render it or just rotate the whole result by 45 degrees, but this brings a whole bag of other issues depending on what you want to do with it
CodePudding user response:
Here is one way to do it (most of this is boiler plate for setting up the containing frame and panel). It uses the sine and cosine
methods to compute the points of a unit
circle. These points are then adjusted by first multiplying by the radius
of the desired larger circle and then centering it in the panel by adding the center x,y
offsets.
The only really special things it contains is 1) that the outer circles are guaranteed to be spaced apart by a distance of one of their diameters. So if the number of circles decreases the size increases. This can be adjusted as you see fit. 2) I used RenderingHints
to visually smooth out the curves. And finally 3) I added a simple WheelListener
with arbitrary limits so you could see the changes when moving the mouse wheel up or down. This modifies NCIRCLES
(something one should not do with constants) and then repaints the panel. It is easily removed.
public class CircleBorder extends JPanel {
JFrame frame = new JFrame("Circle Border");
static int BIG_RADIUS = 200;
static int NCIRCLES = 60;
static int WIDTH = 500;
static int HEIGHT = 500;
public static void main(String[] args ) {
SwingUtilities.invokeLater(()->new CircleBorder().start());
}
public void start() {
addMouseWheelListener((we)-> {
int rot = we.getWheelRotation();
if (NCIRCLES < 70 && rot > 0 || NCIRCLES > 7 && rot < 0) {
NCIRCLES = rot;
}
repaint();
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
// center on screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH,HEIGHT);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int centerX = WIDTH/2;
int centerY = HEIGHT/2;
double angleIncrement = 2*Math.PI/NCIRCLES;
g2d.setColor(Color.RED);
// the next two lines adjusts the little radius so that each
// outer circle will be one diameter apart.
int bigD = (int)(Math.PI*2*BIG_RADIUS);
int littleRadius = bigD/(NCIRCLES*4);
// compute the x and y coordinates of the center of the outer circles.
// and iterate once around the circle based on the computed angle above
// to draw the circumference. The little radius is subtracted to ensure
// the center of those circles lies on the generated outer circle.
double angle = 0;
for (int i = 0; i < NCIRCLES; i ) {
int x = (int)(centerX Math.cos(angle)*BIG_RADIUS) - littleRadius;
int y = (int)(centerY Math.sin(angle)*BIG_RADIUS) - littleRadius;
g2d.fillOval(x,y,littleRadius*2,littleRadius*2);
angle = angleIncrement;
}
g2d.dispose();
}
}