Moving Objects along an arc path with java graphic

2019-06-12 12:15发布

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 :https://stackoverflow.com/questions/36917560/moving-rectangle-spiral-animation-java?noredirect=1#comment61428911_36917560

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.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 class SpiralPath2 extends Path2D.Double {

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

            int centerX = size / 2+200;
            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;

        private SpiralPath2 spiralPath2;
        private final Rectangle box2;

        private List<Point2D> points2;
        private double angle2;
        private Point2D pos2;
        private int index2;

        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();
            }

            spiralPath2 = new SpiralPath2(200);
            box2 = new Rectangle(0, 0, 10, 10);

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

            pos = points.get(0);
            pos2 = points2.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);
                    int index2 = Math.min(Math.max(0, (int) (points2.size() * progress)), points2.size() - 1);

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

                     if (index2 < points2.size() - 1) {
                        angle2 = angleTo(pos2, points2.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(500, 400);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            applyQualityRenderingHints(g2d);

            g2d.translate(20, 50);
            g2d.draw(spiralPath);
            g2d.draw(spiralPath2);
            AffineTransform at = new AffineTransform();
             AffineTransform at2 = new AffineTransform();

            if (pos != null &&pos2!=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);



            }

            Rectangle bounds2 = box2.getBounds();
                at2.rotate(angle2, (bounds2.width / 2), (bounds2.width / 2));
                Path2D player2 = new Path2D.Double(box2, at2);

                g2d.translate(pos2.getX() - (bounds2.width / 2)+50, pos2.getY() - (bounds2.height / 2));
                g2d.setColor(Color.RED);
                g2d.draw(player2);
            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);

    }

}

2条回答
爷的心禁止访问
2楼-- · 2019-06-12 12:59

Try a little program (with no timers no nothing just this plane):

 protected void paintComponent(Graphics g) {
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D)g;
    g2d.setColor(Color.BLACK);
    g2d.drawArc(200,200,200,200,0,90);
    g2d.setColor(Color.magenta);
    for(double t=0; t<Math.PI/2; t+=Math.PI/100) {
      int x = 300 + (int)(100 * Math.cos(t));
      int y = 300 + (int)(100 * Math.sin(t));
      g.fillOval(x, y , 5 , 5);
    }
}

If your arc is 200 wide and high and the arc goes from 0 to 90 (from the right x axis) this should draw the points on the arc.

I think you can generalize this to whatever center you have what width/height etc.

You can also change the angle

int y = 300 + (int)(100 * Math.sin(-t));

if you want to draw backwards.

查看更多
成全新的幸福
3楼-- · 2019-06-12 13:21

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);

    }

}
查看更多
登录 后发表回答