2

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));
  }
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
horse9irl
  • 23
  • 4
  • Well, [this is one idea](https://stackoverflow.com/questions/12964983/rotate-image-around-character-java/12971987#12971987) – MadProgrammer Oct 31 '17 at 02:09
  • Your basic question is this. "Given a circle around another object, how to do find a point on that circle based on a given angle from the centre?" Now, if you can answer this question (find the point on a circle), then you you have the answer for you question, albeit, at a basic level. The rest is trivial – MadProgrammer Oct 31 '17 at 02:12
  • Is this specifically a Swing question? – user1803551 Oct 31 '17 at 07:42

2 Answers2

0

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...

Orbit

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

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
0

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();
        }
    }
}
teppic
  • 7,051
  • 1
  • 29
  • 35