I'm quite new to Java and want to program an easy sun system where the moon rotates around the earth and the earth around the sun.
Everything works well except the moon doesn't want to move correctly :/
Because the earth diverges from the moon's initial position, the rotation radius of the moon grows accordingly to that distance. And again when the earth gets closer to the moons inertial position, the rotation radius decreases.
If the initial position is (0;0), it works but the moon hits the sun...
So how can I keep the distance between earth and moon constant?
I'm using AffineTransforms and here is a snippet of my code ;)
Thanks in advance!
Ellipse2D.Double MoonFrame = new Ellipse2D.Double(orbitEarth + orbitMoon - radiusMoon, -radiusMoon, radiusMoon*2, radiusMoon*2);
for (int i = 0; i < 360; i++)
{
theta += Math.PI/30;
AffineTransform TransformMoon = AffineTransform.getRotateInstance(theta,TransformEarth.getTranslateX(),TransformEarth.getTranslateY());
g2d.fill(TransformMond.createTransformedShape(MoonFrame));
}
So, your basic question comes down to "how do I find a point on a circle for a give angle" ... seriously, it's that simple
Based on many hours of googling and trial and error, I basically use the following, more or less.
protected Point pointOnCircle() {
double rads = Math.toRadians(orbitAngle - 180); // Make 0 point out to the right...
int fullLength = Math.round((outterRadius));
// Calculate the outter point of the line
int xPosy = Math.round((float) (Math.cos(rads) * fullLength));
int yPosy = Math.round((float) (Math.sin(rads) * fullLength));
return new Point(xPosy, yPosy);
}
The rest basically comes down to properly handling the compounding nature of transformations,
Basically, this takes a base Graphics context, applies the translation to it (the Earth's position) and creates two other contexts off it to apply additional transformations, one for the Earth and one for the moon...
Graphics2D g2d = (Graphics2D) g.create();
int yPos = (getHeight() - size) / 2;
// Transform the offset
g2d.transform(AffineTransform.getTranslateInstance(xPos, yPos));
Graphics2D earthG = (Graphics2D) g2d.create();
// Rotate around the 0x0 point, this becomes the center point
earthG.transform(AffineTransform.getRotateInstance(Math.toRadians(angle)));
// Draw the "earth" around the center point
earthG.drawRect(-(size / 2), -(size / 2), size, size);
earthG.dispose();
// Removes the last transformation
Graphics2D moonG = (Graphics2D) g2d.create();
// Calclate the point on the circle - based on the outterRadius or
// distance from the center point of the earth
Point poc = pointOnCircle();
int moonSize = size / 2;
// This is only a visial guide used to show the position of the earth
//moonG.drawOval(-outterRadius, -outterRadius, outterRadius * 2, outterRadius * 2);
moonG.fillOval(poc.x - (moonSize / 2), poc.y - (moonSize / 2), moonSize, moonSize);
moonG.dispose();
g2d.dispose();
And because I know how much that would have you scratching your head, a runnable example...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.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 {
private double angle;
private double orbitAngle;
private int xPos = 0;
private int size = 20;
private int outterRadius = size * 2;
private int delta = 2;
public TestPane() {
new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += delta;
if (xPos + size >= getWidth()) {
xPos = getWidth() - size;
delta *= -1;
} else if (xPos < 0) {
xPos = 0;
delta *= -1;
}
angle += 4;
orbitAngle -= 2;
repaint();
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected Point pointOnCircle() {
double rads = Math.toRadians(orbitAngle - 180); // Make 0 point out to the right...
int fullLength = Math.round((outterRadius));
// Calculate the outter point of the line
int xPosy = Math.round((float) (Math.cos(rads) * fullLength));
int yPosy = Math.round((float) (Math.sin(rads) * fullLength));
return new Point(xPosy, yPosy);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int yPos = (getHeight() - size) / 2;
// Transform the offset
g2d.transform(AffineTransform.getTranslateInstance(xPos, yPos));
Graphics2D earthG = (Graphics2D) g2d.create();
// Rotate around the 0x0 point, this becomes the center point
earthG.transform(AffineTransform.getRotateInstance(Math.toRadians(angle)));
// Draw the "earth" around the center point
earthG.drawRect(-(size / 2), -(size / 2), size, size);
earthG.dispose();
// Removes the last transformation
Graphics2D moonG = (Graphics2D) g2d.create();
// Calclate the point on the circle - based on the outterRadius or
// distance from the center point of the earth
Point poc = pointOnCircle();
int moonSize = size / 2;
// This is only a visial guide used to show the position of the earth
//moonG.drawOval(-outterRadius, -outterRadius, outterRadius * 2, outterRadius * 2);
moonG.fillOval(poc.x - (moonSize / 2), poc.y - (moonSize / 2), moonSize, moonSize);
moonG.dispose();
g2d.dispose();
}
}
}
This moves a "Earth" object, which is rotating in one direction and then rotates the moon around it, in the opposite direction
You can simplify your math by concatenating transforms. Work backwards from the last transform to the first, or use preConcatenate to build them in a more natural order.
Compose complex transforms from simple transforms, for example by building an orbital transform from a translate and a rotate:
// Earth transform.
// Set the orbital radius to 1/3rd the panel width
AffineTransform earthTx = AffineTransform.getTranslateInstance(getWidth() / 3, 0);
// Rotate
earthTx.preConcatenate(AffineTransform.getRotateInstance(angle));
Later transforms (e.g. the moon orbiting the earth) can then be built on top of earlier results:
// Moon transform.
// Set the orbital radius to 1/10th the panel width
AffineTransform moonTx = AffineTransform.getTranslateInstance(getWidth() / 10, 0);
// Rotate
moonTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the earth transform
moonTx.preConcatenate(earthTx);
Full example:
public class Orbit {
public static class OrbitPanel extends JComponent {
int width;
int height;
public OrbitPanel(int width, int height) {
this.width = width;
this.height = height;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Clear the background.
g2.setColor(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
// Sun transform. Just centre it in the window.
AffineTransform sunTx = AffineTransform.getTranslateInstance(getWidth() / 2, getHeight() / 2);
// Draw the sun
g2.setTransform(sunTx);
drawBody(g2, 30, Color.YELLOW);
// Orbital period.
// One rotation every 10s.
double percentRotation = System.currentTimeMillis() % 10000 / 10000.0;
// To radians.
double angle = Math.PI * 2 * percentRotation;
// Earth transform.
// Set the orbital radius to 1/3rd the panel width
AffineTransform earthTx = AffineTransform.getTranslateInstance(getWidth() / 3, 0);
// Rotate
earthTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the sun transform
earthTx.preConcatenate(sunTx);
// Draw the earth
g2.setTransform(earthTx);
drawBody(g2, 10, Color.BLUE);
// Moon transform.
// Set the orbital radius to 1/10th the panel width
AffineTransform moonTx = AffineTransform.getTranslateInstance(getWidth() / 10, 0);
// Rotate
moonTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the earth transform (already includes the sun transform)
moonTx.preConcatenate(earthTx);
// Draw the moon
g2.setTransform(moonTx);
drawBody(g2, 5, Color.DARK_GRAY);
}
private void drawBody(Graphics2D g2, int size, Color color) {
g2.setColor(color);
g2.fillOval(-size / 2, -size / 2, size, size);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
JFrame frame = new JFrame("Orbit");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JComponent orbitPanel = new OrbitPanel(250, 250);
frame.add(orbitPanel);
frame.pack();
frame.setVisible(true);
while (true) {
Thread.sleep(20);
orbitPanel.repaint();
}
}
}
Related
I've found something weird when splitting a translate operation around a scaling one with Java Swing. Maybe I'm doing something stupid but I'm not sure where.
In the first version I center the image, scale it and then translate it to the desired position.
In the second version I directly scale the image and then translate to the desired position compensating for having a non centered image.
The two solutions should be equivalent. Also this is important when considering rotations around a point and motion in another.. I've code that does that too... but why this does not work?
Here are the two versions of the code. They are supposed to do the exact same thing but they are not. Here are the screenshots:
First produces: screenshot1
Second produces: screenshot2
I think that the two translation operations in draw1 surrounding the scale operation should be equivalent to the scale translate operation in draw2.
Any suggestion?
MCVE:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.net.URL;
public class Asteroid extends JComponent implements ActionListener {
public static final Dimension FRAME_SIZE = new Dimension(640, 480);
public double x = 200;
public double y = 200;
public int radius = 40;
private AffineTransform bgTransfo;
private final BufferedImage im2;
private JCheckBox draw1Check = new JCheckBox("Draw 1", true);
Asteroid() {
BufferedImage img = null;
try {
img = ImageIO.read(new URL("https://i.stack.imgur.com/CWJdo.png"));
} catch (Exception e) {
e.printStackTrace();
}
im2 = img;
initUI();
}
private final void initUI() {
draw1Check.addActionListener(this);
JFrame frame = new JFrame("FrameDemo");
frame.add(BorderLayout.CENTER, this);
frame.add(BorderLayout.PAGE_START, draw1Check);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Asteroid asteroid = new Asteroid();
}
#Override
public Dimension getPreferredSize() {
return FRAME_SIZE;
}
#Override
public void paintComponent(Graphics g0) {
Graphics2D g = (Graphics2D) g0;
g.setColor(Color.white);
g.fillRect(0, 0, 640, 480);
if (draw1Check.isSelected()) {
draw1(g);
} else {
draw2(g);
}
}
public void draw1(Graphics2D g) {//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
bgTransfo = new AffineTransform();
//centering
bgTransfo.translate(-imWidth / 2.0, -imHeight / 2.0);
//scaling
bgTransfo.scale(stretchx, stretchy);
//translation
bgTransfo.translate(x / stretchx, y / stretchy);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
public void draw2(Graphics2D g) {//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
bgTransfo = new AffineTransform();
//scale
bgTransfo.scale(stretchx, stretchy);
//translate and center
bgTransfo.translate((x - radius) / stretchx, (y - radius) / stretchy);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
Not sure if this question is still really open. Anyway here is my answer.
I think the crucial part to understand this behavior is the difference between AffineTransform.concatenate and AffineTransform.preConcatenate methods. The thing is that resulting transformation depends on the order the sub-transformations are applied.
To quote the concatenate JavaDoc
Concatenates an AffineTransform Tx to this AffineTransform Cx in the most commonly useful way to provide a new user space that is mapped to the former user space by Tx. Cx is updated to perform the combined transformation. Transforming a point p by the updated transform Cx' is equivalent to first transforming p by Tx and then transforming the result by the original transform Cx like this: Cx'(p) = Cx(Tx(p))
compare this with preConcatenate:
Concatenates an AffineTransform Tx to this AffineTransform Cx in a less commonly used way such that Tx modifies the coordinate transformation relative to the absolute pixel space rather than relative to the existing user space. Cx is updated to perform the combined transformation. Transforming a point p by the updated transform Cx' is equivalent to first transforming p by the original transform Cx and then transforming the result by Tx like this: Cx'(p) = Tx(Cx(p))
The scale and translate methods are effectively concatenate. Lets call 3 transformations in your draw1 method C (center), S (scale), and T (translate). So your compound transformation is effectively C(S(T(p))). Particularly it means that S is applied to the T but not to the C so your C does not really center the image. A simple fix would be to change the order of S and C but I think that a more proper fix would be something like this:
public void draw3(Graphics2D g) {
//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
AffineTransform bgTransfo = new AffineTransform();
//translation
bgTransfo.translate(x, y);
//scaling
bgTransfo.scale(stretchx, stretchy);
//centering
bgTransfo.translate(-imWidth / 2.0, -imHeight / 2.0);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
I think the big advantage of this method is that you don't have to re-calculate the T using stretchx/stretchy
I'm currently working on a program which enables user to draw various geometric shapes. However, I got some issues on calculating and placing the angle objects onto my Canvas panel accurately. The angle object is basically an extension of the Arc2D object, which provides a additional method called computeStartAndExtent(). Inside my Angle class, this method computes and finds the necessary starting and extension angle values:
private void computeStartAndExtent()
{
double ang1 = Math.toDegrees(Math.atan2(b1.getY2() - b1.getY1(), b1.getX2() - b1.getX1()));
double ang2 = Math.toDegrees(Math.atan2(b2.getY2() - b2.getY1(), b2.getX2() - b2.getX1()));
if(ang2 < ang1)
{
start = Math.abs(180 - ang2);
extent = ang1 - ang2;
}
else
{
start = Math.abs(180 - ang1);
extent = ang2 - ang1;
}
start -= extent;
}
It is a bit buggy code that only works when I connect two lines to each other, however, when I connect a third one to make a triangle, the result is like the following,
As you see the ADB angle is the only one that is placed correctly. I couldn't figure how to overcome this. If you need some additional info/code please let me know.
EDIT: b1 and b2 are Line2D objects in computeStartAndExtent() method.
Thank you.
There are some of things that can be made to simplify the calculation:
Keep the vertices ordered, so that it is always clear how to calculate the vertex angles pointing away from the corner
Furthermore, always draw the polygon to the same direction; then you can always draw the angles to the same direction. The example below assumes the polygon is drawn clockwise. The same angle calculation would result in the arcs drawn outside given a polygon drawn counterclockwise.
Example code; is not quite the same as yours as I don't have your code, but has similar functionality:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Polygon extends JPanel {
private static final int RADIUS = 20;
private final int[] xpoints = {
10, 150, 80, 60
};
private final int[] ypoints = {
10, 10, 150, 60
};
final Arc2D[] arcs;
Polygon() {
arcs = new Arc2D[xpoints.length];
for (int i = 0; i < arcs.length; i++) {
// Indices of previous and next corners
int prev = (i + arcs.length - 1) % arcs.length;
int next = (i + arcs.length + 1) % arcs.length;
// angles of sides, pointing outwards from the corner
double ang1 = Math.toDegrees(Math.atan2(-(ypoints[prev] - ypoints[i]), xpoints[prev] - xpoints[i]));
double ang2 = Math.toDegrees(Math.atan2(-(ypoints[next] - ypoints[i]), xpoints[next] - xpoints[i]));
int start = (int) ang1;
int extent = (int) (ang2 - ang1);
// always draw to positive direction, limit the angle <= 360
extent = (extent + 360) % 360;
arcs[i] = new Arc2D.Float(xpoints[i] - RADIUS, ypoints[i] - RADIUS, 2 * RADIUS, 2 * RADIUS, start, extent, Arc2D.OPEN);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(160, 160);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawPolygon(xpoints, ypoints, xpoints.length);
Graphics2D g2d = (Graphics2D) g;
for (Shape s : arcs) {
g2d.draw(s);
}
}
public static void main(String args[]){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Polygon());
frame.pack();
frame.setVisible(true);
}
});
}
}
Results in:
Hello I would like to prevent graphics drawing and drawing again but I don't know how to do, I just want my panel delete all painted graphics and restart with same code. I tried some methods posted here but no one does the job.
public class Main extends JPanel implements ActionListener {
Timer timer;
private double angle = 444;
private double scale = 1;
private double delta = 0.0001;
RoundRectangle2D.Float r = new RoundRectangle2D.Float();
int counter = 0;
public Main() {
timer = new Timer(55, this);
timer.start();
}
public void paint(Graphics g) {
counter++;
int h = getHeight();
int w = getWidth();
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(randomNumber(0, 155), randomNumber(0, 255),randomNumber(0, 155), randomNumber(0, 255)));
drawCircles(g2d, getWidth()/2, getHeight()/2, 250);
if(counter > 200){
g2d.clearRect (0, 0, getWidth(), getHeight());
super.paintComponent(g2d);
counter = 0;
}
}
public int randomNumber(int min, int max) {
int c = new Random().nextInt((max - min) + 1);
return c;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setUndecorated(true);
Dimension dim = new Dimension(Toolkit.getDefaultToolkit()
.getScreenSize().width, Toolkit.getDefaultToolkit()
.getScreenSize().height);
frame.setSize(dim);
frame.setLocation(0, 0);
frame.setBackground(new Color(0, 0, 0, 255));
frame.add(new Main());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void drawCircles(Graphics graphics, int xMid, int yMid, int radius) {
// end recursion
if(radius < 5)
return;
// Draw circle
// start recursion
//left
drawCircles(graphics, xMid-radius, yMid, radius / 2);
((Graphics2D) graphics).rotate(angle);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
//right
drawCircles(graphics, xMid+radius, yMid, radius / 2);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
}
public void actionPerformed(ActionEvent e) {
if (scale < 0.01)
delta = -delta;
else if (scale > 0.99)
delta = -delta;
scale += delta;
angle += 0.001;
repaint();
}
}
I am not sure I understand you fully, but you can use a JToggleButton (for example) where is the toggle button is down it prevents drawing. I can see something like this inside your drawCircles() method:
void drawCircles(Graphics graphics, int xMid, int yMid, int radius)
{
if(!toggleBtn.isSelected() // the toggle button is pressed
{
// draw something
}
}
In your example, you are drawing two circles and two ovals. If I understood you correctly, you want to be able to pause in the middle of the method, for example, and only draw the first circle. Then, at some point, you want to continue drawing the two ovals and the remaining circle. Unfortunately, you cannot do that. You cannot stop (or pause) a method in the middle of it.
Methods have to execute to completion (whether to the end, or an exception is thrown). However, you can create some kind of task to draw ONE shape (for example, a circle). If you create multiple tasks, you can draw many circles. To accomplish this, you will need to learn about Concurrency and probably about Java Tasks. You can have these tasks execute in some kind of order and because of concurrency, you could pause and resume these drawing tasks the way I think you would want.
I have a simple game animation made in java. It is of three planets rotating around an axis. Each planet is an instance of the class Planet and they have an update method which, every time it is run, the orbit's rotation angle increases and the position is updated acording to the angle and a few predetermined variables like distance from the "sun". From here, you can determine the position of the planet with simple trigonometry. In this case:
Sin(angle) = op/hyp = y/distance
therefore
Sin(angle)*hyp = op
Cos(angle) = ady/hyp = x/distance
therefore
Cos(angle)*hyp = ady
where the hypothenuse is the distance to the sun and the adyacent and oposite sides are the x and y values respectively. I figured this would work, until I tried it out. It gave me an eliptical rotation. Here is the code that updates the planet's rotation (orbit center is the sun's center position):
position.x = ((Math.cos(orbitAngle) * orbitDistance) + orbitCenter.x);
position.y = ((Math.sin(orbitAngle) * orbitDistance) + orbitCenter.y);
What could be wrong?
EDIT:
I realized this problem by placing an object with its center in the position specified by orbit center
Here is the full code of the planet class:
public class Planet
{
protected Image image;
protected Vector2 position;
protected final Vector2 orbitCenter;
protected float rotation;
protected Vector2 imageSize;
protected final float rotationSpeed;
protected final float orbitDistance;
protected float orbitAngle;
protected final float orbitAngleSpeed;
public Planet(Image image, float orbitDistance, float rotationSpeed, Vector2 orbitCenter, float orbitAngleSpeed)
{
this.image = image;
this.position = new Vector2(orbitCenter.x, orbitCenter.y - orbitDistance);
this.orbitCenter = orbitCenter;
this.rotation = 0;
this.imageSize = new Vector2(image.getWidth(null), image.getHeight(null));
this.rotationSpeed = rotationSpeed;
this.orbitDistance = orbitDistance;
this.isMouseOver = false;
this.isPressed = false;
this.orbitAngle = 0;
this.orbitAngleSpeed = orbitAngleSpeed;
}
public void Update()
{
orbitAngle += orbitAngleSpeed;
if(orbitAngle > Math.PI * 2)
orbitAngle %= Math.PI * 2;
position.x = ((Math.cos(orbitAngle) * orbitDistance) + orbitCenter.x);
position.y = ((Math.sin(orbitAngle) * orbitDistance) + orbitCenter.y);
}
public void Draw(Graphics2D g)
{
g.rotate(rotation, position.x + imageSize.x / 2, position.y + imageSize.y / 2);
g.drawImage(image, (int)position.x, (int)position.y, null);
g.rotate(-rotation, position.x + imageSize.x / 2, position.y + imageSize.y / 2);
}
}
Here is the class that tests the planet class. You can download the jar it needs to work from here: foxtailgames.net/AppletSource.jar. Here is the tester class (you will probably have to import a few things though if you do it in eclipse or netbeans it will give you the imports):
public class PlanetTest extends AppletCore
{
public void resizeScreen() {resize(800, 800);}
Image center;
Planet p;
public void LoadContent()
{
p = new Planet(loadImage("images/GameMenuCircles/Planet1.png"), 100f, 0.02f, new Vector2(400, 400), 0.005f);
center = loadImage("images/GameMenuCircles/Center.png");
}
public void Update(GameTime gameTime)
{
p.Update();
}
public void Draw(Graphics2D g, GameTime gameTime)
{
g.drawImage(center, 400 - center.getWidth(null)/2, 400 - center.getWidth(null)/2, null);
p.Draw(g);
g.setColor(Color.green);
g.drawLine(400, 400, 500, 400);
g.drawLine(400, 400, 400, 500);
g.drawLine(400, 400, 300, 400);
g.drawLine(400, 400, 400, 300);
g.setColor(Color.white);
}
}
Your rotation is set to 0 in the above so i assume you are not rotating the picture at the moment. What i think is happening is the orbit circle you are producing is fine, but the location you are drawing the planet is off.
Below is an image of how Swing would draw the circle, so the overlap you experience is because of this.
You need to adjust the position you draw the circle by how half the width so it sits over the center of the orbit.
EDIT: You've alter some code but what you need to change is the draw method of he planet:
public void Draw(Graphics2D g) {
g.rotate(rotation, position.x + imageSize.x / 2, position.y + imageSize.y / 2);
g.drawImage(image, (int)position.x, (int)position.y, null); //here
g.rotate(-rotation, position.x + imageSize.x / 2, position.y + imageSize.y / 2);
}
This line needs to be:
g.drawImage(image, (int)position.x - imageSize.width, (int)position.y - imageSizee.height, null); //here
You might compare your result to this AnimationTest that uses the same parametric equation of a circle. Because the orbital radius is a function of the enclosing panel's dimensions, the orbit is circular only when w equals h. Resize the frame, or set HIGH = WIDE, to see the effect.
I've got a JPanel I'm drawing onto and I'm trying to get it so that you can use the mouse wheel to "zoom" in and out by modifying the variable scale. Right now what happens is you can zoom in or out but all of the drawing shifts to the right and downwards when you zoom in and then back up and left when zooming out.
I want to make it so that it adjusts as if you were zooming in on the point at the center of the JPanel but I can't figure out how to calculate the right offset to translate by…
Anybody got an idea of how to do this, or even better a cleaner way of achieving this whole pan and zoom ability?
I am basically plotting a bunch of coordinates that came from a system where 0,0 is in the lower left corner and fall between the bounds:
xMin = 661208
xMax = 662618
yMin = 4291657
yMax = 4293285
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
double scale = details.getScale();
double xOffset = details.getxOffset();
double yOffset = details.getyOffset();
g2.scale(scale, -scale);
// Dividing offset by scale makes panning 1:1 with the cursor still. yMax to account for the
// fact we needed to flip around the y axis to make it right-side up.
g2.translate((-xMin + xOffset / scale), (-yMax + yOffset / scale));
// Code to draw stuff goes here. It uses coordinates between xMin-xMax and yMin-yMax to draw.
.
.
.
}
Here is an example with the wheel:
public class GraphicsOnly extends JPanel implements MouseWheelListener {
Shape[] shapes;
Dimension size;
double scale = 1.0;
private static int source = 100;
public GraphicsOnly() {
addMouseWheelListener(this);
size = new Dimension(10,10);
setBackground(new Color(240,200,200));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(shapes == null) initShapes();
// Keep shapes centered on panel.
double x = (getWidth() - scale*size.width)/2;
double y = (getHeight() - scale*size.height)/2;
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.scale(scale, scale);
g2.setPaint(Color.blue);
g2.draw(at.createTransformedShape(shapes[0]));
g2.setPaint(Color.green.darker());
g2.draw(at.createTransformedShape(shapes[1]));
g2.setPaint(new Color(240,240,200));
g2.fill(at.createTransformedShape(shapes[2]));
g2.setPaint(Color.red);
g2.draw(at.createTransformedShape(shapes[2]));
}
public Dimension getPreferredSize() {
int w = (int)(scale*size.width);
int h = (int)(scale*size.height);
return new Dimension(w, h);
}
private void initShapes() {
shapes = new Shape[3];
int w = getWidth();
int h = getHeight();
shapes[0] = new Rectangle2D.Double(w/16, h/16, w*7/8, h*7/8);
shapes[1] = new Line2D.Double(w/16, h*15/16, w*15/16, h/16);
shapes[2] = new Ellipse2D.Double(w/4, h/4, w/2, h/2);
size.width = w;
size.height = h;
}
public static void main(String[] args) {
GraphicsOnly app = new GraphicsOnly();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(app));
f.setSize(400, 400);
f.setLocation(200,200);
f.setVisible(true);
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
// TODO Auto-generated method stub
if(e.getWheelRotation() >0){
source = source-e.getScrollAmount();
}else{
source = source+e.getScrollAmount();
}
scale = source/100.0;
System.out.println(scale);
repaint();
revalidate();
}
}