Getting a Circle to orbit around another - java

I have been doing a ton of research and none of the questions I found really answered my question and that is why I am making this post.
I want to create a program that will have a circle, a "planet" orbit around a another circle, a "Sun."
I have the static gui set up, but nothing I have found in my book or online really helps solves the orbit problem. Any ideas?
NOTE: eventually the program needs to be multithreaded (one for the planet and one for the Sun) but I want to break the problem down before I get back into trying to get that to work so for now please disregard it.
GUI:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class PlanetsGUI extends JPanel
{
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(Color.BLACK);
paintSun(g);
paintPlanet(g);
}
public void paintSun(Graphics g)
{
super.paintComponent(g);
//create circle and fill it as yellow to represent the sun
g.setColor(Color.YELLOW);
g.drawOval(100, 75, 75, 75);
g.fillOval(100, 75, 75, 75);
} //end paintSun
public void paintPlanet(Graphics g)
{
//create circle and fill it as blue to represent the orbiting planet
g.setColor(Color.BLUE);
g.drawOval(35, 50, 50, 50);
g.fillOval(35, 50, 50, 50);
}//end paintPlanet
}//end class PlanetsGUI
MAIN:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
public class OrbitingPlants_main
{
private static final ExecutorService execute + Executors.newFixedThreadPool(2);
public static void main(String[] args)
{
PlanetsGUI planet = new PlanetsGUI();
JFrame frame = new JFrame();
frame.setTitle("Orbiting Planets");
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(planet); //add panel onto frame
frame.setVisible(true);
//PlanetsLogic r = new PlanetsLogic();
//Thread sun = new Thread(sun);
//sun.start();
//execute.submit(new );
}//end main
}//end class

You're hard-coding where the images are being drawn, for example:
g.setColor(Color.BLUE);
g.drawOval(35, 50, 50, 50);
g.fillOval(35, 50, 50, 50);
making for images that can never change.
Instead get rid of the hard coded numbers and use variables, values that can change:
g.setColor(Color.BLUE);
// variables used below are fields declared in the class
g.drawOval(planetX, planetY, planetW, planetW); // probably don't need this line
g.fillOval(planetX, planetY, planetW, planetW);
and then change the values in your thread (or better, Swing Timer).
As far as "orbiting", that would be using the equations of a circle (or ellipse if you wanted to be extremely precise), and perhaps using a parametric equation to determine your planetX and planetY values.

Related

Repainting a dirty region interfere with graphics2D clipbounds

In the following reproducer, I am reproducing what I'm seeing in the graphical interface.
I'd like to repaint a dirty region of a canvas (a JPanel), because a full repaint introduces more latencies.
However when the repaint (dirty rectangle) function is invoked, it causes some issues with nested Graphics2D objects. In particular
This code may mis-use clip bounds, but I wasn't expecting this "artifact" upon the creation of the sub (nested) graphic context.
What would be the proper way to deal with clips, nested graphic contexts, and the dirty region (or rectangle) repaint in this situation.
My intuition tells me the code should compute its shape without using the clipping area.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
public class Reproducer {
public static void main(String[] args) {
var comp = new MyJPanel();
var timer = new Timer(2_000, e -> {
SwingUtilities.invokeLater(comp::bug);
});
timer.start();
timer.setRepeats(true);
SwingUtilities.invokeLater(() -> {
var frame = new JFrame("Reproducer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(300, 300));
frame.setVisible(true);
frame.getContentPane().add(comp);
});
}
private static class MyJPanel extends JPanel {
public MyJPanel() {
setSize(600, 600);
setPreferredSize(getSize());
}
#Override
protected void paintComponent(Graphics g) {
var g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(Color.cyan);
g2.fill(g2.getClip());
var graphicsBox = (Graphics2D) g2.create(10, 10, 290, 290);
var clipBounds = graphicsBox.getClipBounds();
graphicsBox.setColor(Color.LIGHT_GRAY);
graphicsBox.setStroke(new BasicStroke(2));
graphicsBox.drawRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);
graphicsBox.setColor(Color.red);
graphicsBox.fill(new Rectangle2D.Double(clipBounds.x + 2,
clipBounds.y + 2,
clipBounds.width - 2,
clipBounds.height - 2));
graphicsBox.dispose();
var graphicsOverlay = (Graphics2D) g2.create(100, 100, 100, 100);
graphicsOverlay.setColor(Color.YELLOW);
graphicsOverlay.fillOval(10, 10, 60, 60);
graphicsOverlay.dispose();
}
public void bug() {
getVisibleRect();
repaint(100, 100, 100, 100);
}
}
}
EDIT by moving the fill above drawRect
So, there are a couple of "mistakes"
I found this...
graphicsBox.drawRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height);
graphicsBox.setColor(Color.red);
graphicsBox.fill(new Rectangle2D.Double(clipBounds.x + 2,
clipBounds.y + 2,
clipBounds.width - 2,
clipBounds.height - 2));
to be a little erroneous, which meant when a clip was applied, the background area was not been filled properly.
The call to super.paintComponent was filling the clip area with the background color of the component, but graphicsBox.fill wasn't filling the entire area, allow part of the background color to be seen.
I also found the result of Graphics2D#create(int, int, int, int) to be a little surprising
The new Graphics object has its origin translated to the specified point (x, y). Its clip area is determined by the intersection of the original clip area with the specified rectangle.
(emphasis added by me)
I mean, honestly, I shouldn't be surprised, but I've just never used these features before so it took me a little by surprise.
So, I corrected the graphicsBox rendering code so that the fill will fill the entire clipping area first and THEN the rectangle is drawn (within the bounding rectangle)
var graphicsBox = (Graphics2D) g2.create(10, 10, 290, 290);
var clipBounds = graphicsBox.getClipBounds();
graphicsBox.setColor(Color.red);
graphicsBox.fill(new Rectangle2D.Double(clipBounds.x,
clipBounds.y,
clipBounds.width,
clipBounds.height));
Collections.shuffle(colors);
graphicsBox.setColor(colors.get(0));
graphicsBox.setStroke(new BasicStroke(2));
graphicsBox.drawRect(clipBounds.x + 1, clipBounds.y + 1, clipBounds.width - 2, clipBounds.height - 2);
graphicsBox.dispose();
nb: The reference to colors is just a list of Colors which I randomise during the paint process, so I can "see" what's going on
Another trick might be to change the background color of the component to make it easier to "see" where things are been updated incorrectly.
When you have issues like this, it's really important to reduce the amount of clutter to see if you can narrow down the problem, I took out most of the paint code to figure out all these "little" things which were combining to give you issues.
Runnable example...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
var comp = new MyJPanel();
var timer = new Timer(2_000, e -> {
SwingUtilities.invokeLater(comp::bug);
});
timer.start();
timer.setRepeats(true);
SwingUtilities.invokeLater(() -> {
var frame = new JFrame("Reproducer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(300, 300));
frame.setVisible(true);
frame.getContentPane().add(comp);
});
}
private static class MyJPanel extends JPanel {
private List<Color> colors;
public MyJPanel() {
setSize(600, 600);
setPreferredSize(getSize());
colors = Arrays.asList(new Color[] {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
});
}
#Override
protected void paintComponent(Graphics g) {
var g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(Color.cyan);
g2.fill(g2.getClip());
var graphicsBox = (Graphics2D) g2.create(10, 10, 290, 290);
var clipBounds = graphicsBox.getClipBounds();
graphicsBox.setColor(Color.red);
graphicsBox.fill(new Rectangle2D.Double(clipBounds.x,
clipBounds.y,
clipBounds.width,
clipBounds.height));
Collections.shuffle(colors);
graphicsBox.setColor(colors.get(0));
graphicsBox.setStroke(new BasicStroke(2));
graphicsBox.drawRect(clipBounds.x + 1, clipBounds.y + 1, clipBounds.width - 2, clipBounds.height - 2);
graphicsBox.dispose();
var graphicsOverlay = (Graphics2D) g2.create(100, 100, 100, 100);
graphicsOverlay.setColor(Color.YELLOW);
graphicsOverlay.fillOval(10, 10, 60, 60);
graphicsOverlay.dispose();
}
public void bug() {
getVisibleRect();
repaint(100, 100, 100, 100);
}
}
}
If you're having "performance" issues, you might consider using a BufferedImage as the primary canvas and update it, then simply paint the image.
Alternatively, it might be time to move over to using BufferStrategy, this is going to get you as close to the "metal" as Java/Swing allows

Java Swing paint() Twice Show Once when Full Screen

I'm making a small game using Java Swing. A class Game extends JPanel and implements ActionListener, a Timer in it calls repaint() to update the screen, then create a JFrame and add Game to it.
Then I noticed when I make the window full screen, the screen is updated once when paint() is called twice. This is weird! Any help would be appreciated.
Here is an SSCCE, the example creates a variable count, and paint count then count+=1; in repaint(). When not full screen, it shows 0 1 2 3 4... When full screen, it shows 4 6 8 10...
Example code here:
package test.swing.FullScreenDropFrameRate;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
class Game extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private int count = 0;
public Game() {
new Timer(1000, this).start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paint(Graphics g) {
// bg
g.setColor(Color.black);
g.fillRect(0, 0, getSize().width, getSize().height);
// info string
g.setColor(Color.white);
g.setFont(new Font("serif", Font.BOLD, 30));
g.drawString(""+count, 0, 30);
count += 1;
g.dispose();
}
}
public class TestFullScreenFrameRate {
public static void main(String[] args) {
JFrame jf = new JFrame("Snake");
jf.setBounds(100, 35, 800, 600); // x, y, width, height
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new Game());
jf.setVisible(true);
}
}
OK, so the problem is that I shouldn't put anything which updates the game state in paint(). Java calls it when needed. Thanks to #Andrew Thompson.
Some other mistakes, improvements and documentation:
1)Documentation - JComponent - paint(): Invoked by Swing to draw components. ... A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.
2)Documentation - Font - SERIF: A String constant for the canonical family name of the logical font "Serif". It is useful in Font construction to provide compile-time verification of the name.
3)Oracle - about Painting
4)Documentation - Graphics - dispose(): For efficiency, programmers should call dispose when finished using a Graphics object only if it was created directly from a component or another Graphics object.
5)A Java Game Tutorial on Youtube: Bad example! DO NOT use paint() and g.dispose() here!!!

Java GUI does not appear

This is for my university project and I am busting my brain around why my Java GUI does not work. This is the situation: the code compiles and executes without an issue.
This code should create 300 X 300 frame center the desktop and create circle and it print my name underneath.
I got it working until the frame, but no circle
package gui;
import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
public class GUI extends JFrame{
public void Paint (Graphics g){
super.paintComponents(g);
g.setColor(Color.yellow);
g.fillOval(50, 50, 200, 200);
g.setColor(Color.BLACK);
g.drawArc(75, 60, 150, 150, -25, -125);
g.fillOval(100, 100, 25, 25);
g.fillOval(175, 100, 25, 25);
g.setColor(Color.BLUE);
g.setFont(new Font("Serif", Font.BOLD,18));
g.drawString("My Nanme is BOB", 33, 275);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
GUI GUI = new GUI() ;
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setSize(300,300);
GUI.setTitle("BOB's GUI App");
GUI.setVisible(true);
GUI.setLocationRelativeTo(null);
I really appreciate your output. also please give me a hint why it does not work
Java is case-sensitive :
public void Paint (Graphics g)
Will never override
public void paint (Graphics g)
In your main, you have written
GUI GUI = new GUI() ;
You should make the second GUI something else. For example,
GUI gui = new GUI();
Your paint() method should have a lowercase 'P'. As you currently have it, you're not overriding the existing JFrame method.
To avoid such issues in the future, I would investigate the #Overrides annotation. If you use this on the above, it will tell you you're not overriding anything!

Window freezes for one time when String is drawn

While I was making a 2D Game in Java, a (atleast to me) very strange problem occured. I've provided a runnable, shortened example which reproduces my problem. When the x-coordinate of the red square is between 100 and 120 it should draw the string "Sample Text" above the square. However, you will see if you run the code, the window freezes completely for a few seconds. After the lag you can go over the area without problems, and the text will be shown. This problem only occurs when the program draws a String above the square. If I change the code so only another square appears above the red one, there's no lag. (I commented that in my code)
Any help would be appreciated.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
public class MyExample extends JApplet {
int x = 10;
int y = 150;
public void init() {
setFocusable(true);
requestFocus();
Action right = new moveRight();
Action left = new moveLeft();
JRootPane rootPane = getRootPane();
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
rootPane.getActionMap().put("right", right);
rootPane.getActionMap().put("left", left);
getContentPane().add(new Paint());
}
protected class moveRight extends AbstractAction {
public void actionPerformed(ActionEvent e) {
x+=3;
repaint();
}
}
protected class moveLeft extends AbstractAction {
public void actionPerformed(ActionEvent e) {
x-=3;
repaint();
}
}
class Paint extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x,y,10,10);
g.setColor(Color.BLACK);
g.drawLine(100,100,100,200);
g.drawLine(129,100,129,200);
if(x>100&&x<120) {
g.setFont(new Font("TimesRoman", Font.PLAIN, 15));
g.setColor(Color.BLACK);
g.drawString("Sample Text",x-30,y-25);
//g.fillRect(x,y-15,10,10); - This work fine if you remove the g.setFont and the drawString
}
}
}
}
This has to do with the fact that you are trying to load the font within the paintComponent method AND the underlying API trying to load the font and it's details before it can paint them.
I did think you could just pre-cache the font using something like...
class Paint extends JPanel {
private Font paintFont;
public Paint() {
paintFont = new Font("TimesRoman", Font.PLAIN, 15);
setFont(paintFont);
}
But in my testing, this still didn't work, what I actually ended up doing was adding a call to getFontMetrics, which seems to force the API to load the font and it's properties into memory, making it render immediately, for example
class Paint extends JPanel {
private Font paintFont;
public Paint() {
paintFont = new Font("TimesRoman", Font.PLAIN, 15);
setFont(paintFont);
getFontMetrics(paintFont);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, y, 10, 10);
g.setColor(Color.BLACK);
g.drawLine(100, 100, 100, 200);
g.drawLine(129, 100, 129, 200);
if (x > 100 && x < 120) {
System.out.println("...");
//g.setFont(paintFont);
g.setColor(Color.BLACK);
g.drawString("Sample Text", x - 30, y - 25);
//g.fillRect(x,y-15,10,10); - This work fine if you remove the g.setFont and the drawString
}
}
}
Now, this will make your application load slightly slower, but will allow it to run faster as you've moved the loading of the font out of the paint cycle
On my system TimesRoman doesn't exist.
I used Times New Roman with no problems. I also tried a few other fonts and they worked no problem. So I guess specifying an invalid font name causes a hiccup?
I also created the font once and cached it:
Font font = new Font("Times New Roman", Font.PLAIN, 15);
and then in the painting method used:
g.setFont( font );
Also, don't forget the super.paintComponent(g);
This question contains the code I used to list the fonts on my machine: Java JTextArea font

Program to create shapes and show them on console

I was given small assignment as below . Can you please throw some light on how to implement this
Write a simple structured program and simple oo program that implements display shape function. Your program should simply print out (to console ) the number if shapes and then ask each shape to display itself which will also cause a line of output to be generated to the console , one for each shape . It is perfectly OK for your main program to create a collection of shapes before on to sorting that collection and displaying the shapes. Your program should support circles , triangles and squares but should use polymorphism so that the main program doesn't know the type of shape it is dealing with but instead treats shapes uniformly
I had created a program to create shapes like below but i'm not sure on how to create the shapes as mentioned and store them in collection and iterate to display these shapes on console. I was told not to use the database for storing shapes
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;
public class DrawShapes extends JApplet {
public void paint(Graphics g) {
g.setColor(Color.RED);
// drawing string
g.drawString("Hello World!", 100, 100);
// drawing rectangle
g.draw3DRect(120, 120, 120, 120, true);
g.setColor(Color.blue);
g.fill3DRect(120, 120, 120, 120, true);
// drawing circle
g.drawOval(240, 240, 120, 120);
g.setColor(Color.CYAN);
g.fillOval(240, 240, 120, 120);
// drawing square
g.drawRect(350, 350, 250, 100);
g.setColor(Color.magenta);
g.fillRect(350, 350, 250, 100);
// drawing trinale
}
}
Just an idea how to do it. Notice, that the drawing is hidden from the shapes collection.
interface Drawable {
public void draw(Graphics g);
}
class DrawableSquare implements Drawable{
public DrawableSquare(int x, int y, int width) { ... }
public void draw(Graphics g)
{
g.fillRect(x, y, width, width);
}
}
class Screen {
Collection<Drawable> drawables;
public void paint(Graphics g) {
for (Drawable dr: drawables) {
dr.draw(g);
}
}
}

Categories

Resources