简体   繁体   English

使用Java图形沿着弧形路径移动对象

[英]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. 我有3天的时间以螺旋方式移动物体,但我认为我必须寻找问题的一小部分,即以弧形移动物体。 See my Question : https://stackoverflow.com/questions/36917560/moving-rectangle-spiral-animation-java?noredirect=1#comment61428911_36917560 见我的问题: 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 ) 这是我获取新的X和Y点的方法(算法不是代码)

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); 1-在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. 2-在相同的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); 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 3-因为我使用的是计时器,并且需要一个ActionListener,所以actionPerformed方法将更新矩形的Start_point_X,Start_point_Y的值

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 ). 这是问题,我无法为将解决问题的移动部分的对象计算New X,Y值(我知道这些单词不是专业单词)。

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) x = center_X +半径* cos(角度)

y = center_y + radius * sin(angle) y = center_y +半径* sin(角度)

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

    }

}

So based on this idea , you can take advantage of the functionality already available in the 2D Graphics API. 因此,基于此想法 ,您可以利用2D图形API中已经可用的功能。

The difficult part would be to get your spiral shape setup as a Path object, lucky for us, the API is very flexible ... 困难的部分是将螺旋形状设置为Path对象,对我们来说幸运的是,API非常灵活...

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

}

精神

Now we have that, the rest is (relatively) simple, as it follows a well know pattern... 现在我们有了,其余的(相对)简单,因为它遵循众所周知的模式...

精神

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

    }

}

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. 如果圆弧的宽度为200且高,并且圆弧从0到90(从右x轴开始),则应在圆弧上绘制点。

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. 如果您想向后退。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM