Dodo Dodo Dodo Dodo - 7 months ago 138
Java Question

Moving Objects along an arc path with java graphics

I had 3 days looking for moving on objects on a spiral way but I figured that I have to look for the small part of problem which is moving objects on arc.
See my Question :Moving rectangle spiral Animation Java

Now, the problem is how i can calculate the points that are exists on the arc. this is my Approach to get the new X and Y Points( Algorithm not code )

1- Draw arc using this method in JAVA
g2d.fillArc(start_point_X_Arc,start_point_Y_Arc,width_of_arc,height_of_arc,start_angle,end_angle);

2- Draw the Object on the Same Start_point_X,Start_point_Y. And here I will draw a rectangle using this method

g2d.drawRect(start_point_X_Rect, Start_point_Y_Rect, 10, 10);

3- Because I'm using a timer and it needs an ActionListener the actionPerformed method will update the Values of Start_point_X, Start_point_Y for the rectangle

AND HERE IS THE PROBLEM I can't calculate the values of the New X,Y values for the object which will do the moving part of the problem ( I know that these word are not professional words ).

Because of that I search how to calculate points on arc and I find the Parametric Equations for a circle

x = center_X + radius * cos(angle)

y = center_y + radius * sin(angle)

and I know that these equation might be used in someway to get the new points but i'm not good in math.

Therefore,I need help with doing the object moving in arc path and i think this would help me to do an object moving in spiral path. If my algorithm is wrong or anything is wrong please give me advice to do it in a simple way.

This is a code that I made it to draw an arc & rectangle and the rectangle is moving in diagonal path.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.Timer;

public class Moving extends JPanel implements ActionListener{

Timer tm=new Timer(5,this);
int x =400,velx= 2;
int y=300;

public static void main(String[] args) {
JFrame frame = new JFrame("Moving Rectanlge");
frame.add(new Moving());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(900, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLACK);
g2d.fillArc(200,200,200,200,0,90);
g2d.drawRect(x, y, 10, 10);
tm.start();
}

@Override
public void actionPerformed(ActionEvent ae) {
x=x+velx;
y=y+velx;
repaint();
}

Answer

So based on this idea, you can take advantage of the functionality already available in the 2D Graphics API.

The difficult part would be to get your spiral shape setup as a Path object, lucky for us, the API is very flexible ...

public class SpiralPath extends Path2D.Double {

    public SpiralPath(int size) {
        int numIterations = 5;
        int arcGrowDelta = (size / numIterations) / 2;
        int arcWidth = 0;

        int centerX = size / 2;
        int centerY = size / 2;
        moveTo(centerX, centerY);

        for (int i = 0; i < numIterations; i++) {
            append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
            arcWidth += arcGrowDelta;
            append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
        }
    }

}

Sprial

Now we have that, the rest is (relatively) simple, as it follows a well know pattern...

Sprial

package javaapplication1.pkg005;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public class SpiralPath extends Path2D.Double {

        public SpiralPath(int size) {
            int numIterations = 5;
            int arcGrowDelta = (size / numIterations) / 2;
            int arcWidth = 0;

            int centerX = size / 2;
            int centerY = size / 2;
            moveTo(centerX, centerY);

            for (int i = 0; i < numIterations; i++) {
                append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
                arcWidth += arcGrowDelta;
                append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
            }
        }

    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private SpiralPath spiralPath;
        private final Rectangle box;

        private List<Point2D> points;
        private double angle;
        private Point2D pos;
        private int index;

        protected static final double PLAY_TIME = 5000; // 5 seconds...

        private Long startTime;

        public TestPane() {
            spiralPath = new SpiralPath(150);
            box = new Rectangle(0, 0, 10, 10);

            points = new ArrayList<>(25);
            PathIterator pi = spiralPath.getPathIterator(null, 0.01);
            while (!pi.isDone()) {
                double[] coords = new double[6];
                switch (pi.currentSegment(coords)) {
                    case PathIterator.SEG_MOVETO:
                    case PathIterator.SEG_LINETO:
                        points.add(new Point2D.Double(coords[0], coords[1]));
                        break;
                }
                pi.next();
            }

            pos = points.get(0);
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {

                    if (startTime == null) {
                        startTime = System.currentTimeMillis();
                    }
                    long playTime = System.currentTimeMillis() - startTime;
                    double progress = playTime / PLAY_TIME;
                    if (progress >= 1.0) {
                        progress = 1d;
                        ((Timer) e.getSource()).stop();
                    }

                    int index = Math.min(Math.max(0, (int) (points.size() * progress)), points.size() - 1);

                    pos = points.get(index);
                    if (index < points.size() - 1) {
                        angle = angleTo(pos, points.get(index + 1));
                    }
                    repaint();
                }
            });

            timer.start();
        }

        protected double angleTo(Point2D from, Point2D to) {
            double angle = Math.atan2(to.getY() - from.getY(), to.getX() - from.getX());
            return angle;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            applyQualityRenderingHints(g2d);
            int x = (getWidth() - spiralPath.getBounds().width) / 2;
            int y = (getHeight() - spiralPath.getBounds().height) / 2;
            g2d.translate(x, y);
            g2d.draw(spiralPath);
            AffineTransform at = new AffineTransform();

            if (pos != null) {

                Rectangle bounds = box.getBounds();
                at.rotate(angle, (bounds.width / 2), (bounds.width / 2));

                Path2D player = new Path2D.Double(box, at);

                g2d.translate(pos.getX() - (bounds.width / 2), pos.getY() - (bounds.height / 2));
                g2d.setColor(Color.RED);
                g2d.draw(player);

            }
            g2d.dispose();
        }

    }

    public static void applyQualityRenderingHints(Graphics2D g2d) {

        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

    }

}
Comments