Java:在圆形路径中移动标签

时间:2012-06-03 19:48:35

标签: java image swing jlabel trigonometry

我正在写一个类似于模拟的精灵,我需要在圆形路径中移动带有图像的标签。我有4个起点;西,北,东,南。标签应以顺时针方式移动。

这是我写的,它运作良好,除了北和南位置逆时针旋转的事实。原因是,我使用了一个单独的trig公式来处理方向2和4。

    int xstart = 450 
    int ystart = 325;
    int h = 0;
    double theta = -0.1047;

    for (int p = 0;; p++)
    {
        int xocir = xstart;
        int yocir = ystart;
        int pocir = 0; // perpendicular and base of outer circle
        int bocir = 0;

        if (direction == 1)//west - clockwise
        {
            pocir = (int) (Math.cos(theta) * (h + 300));
            bocir = (int) (Math.sin(theta) * (h + 300));
        }
        else if (direction == 2)//north - counter-clockwise
        {
            pocir = (int) (Math.sin(theta) * (h + 300));//reversed sin,cos
            bocir = (int) (Math.cos(theta) * (h + 300));
        }
        else if (direction == 3)//east - clockwise
        {
            pocir = (int) (Math.cos(theta) * (h - 300));
            bocir = (int) (Math.sin(theta) * (h - 300));
        }
        else if (direction == 4)//south - counter-clockwise
        {
            pocir = (int) (Math.sin(theta) * (h - 300));
            bocir = (int) (Math.cos(theta) * (h - 300));
        }

        xocir = xocir - pocir;
        yocir = yocir - bocir;
        theta = theta - 0.005;

        setBounds(xocir, yocir, 65, 65);
            }

有没有更有效的方法来解决这个问题?也许是一种更简单的trig方法。我该怎么做才能确保所有动作都是顺时针的?

2 个答案:

答案 0 :(得分:4)

根据方向,只使用顺时针if语句中的一个而不是不同的等式。起始位置由theta的初始值和使用的参数方程控制。


x = circleCenterX - (int) (Math.sin(theta) * radius)
y = circleCenterY - (int) (Math.cos(theta) * radius)
circleCenterX = 450,
circleCenterY = 325,和 radius = 300。

If theta = 0 deg, then
(x,y) = (450 - sin(0) * 300, 325 - cos(0) * 300)
      = (450, 25) which is north of (450, 325)

If theta = 90 deg, then
(x,y) = (450 - sin(90) * 300, 325 - cos(90) * 300)
      = (150, 325) which is west of (450, 325)

If theta = 180 deg, then
(x,y) = (450 - sin(180) * 300, 325 - cos(180) * 300)
      = (450, 625) which is south of (450, 325)

If theta = 270 deg, then
(x,y) = (450 - sin(270) * 300, 325 - cos(270) * 300)
      = (750, 325) which is east of (450, 325)

以下课程概括了这个想法:

import java.awt.Dimension;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class RotatingLabel extends JLabel {
    private static final long serialVersionUID = 474652899660954020L;
    private final int circleCenterX;
    private final int circleCenterY;
    private final int radius;
    private double theta;
    private final double thetaIncrement;

    public RotatingLabel(String text, //
            int circleCenterX, int circleCenterY, int radius, //
            double initialTheta, double thetaIncrement) {
        super(text);
        this.circleCenterX = circleCenterX;
        this.circleCenterY = circleCenterY;
        this.radius = radius;
        this.theta = initialTheta;
        this.thetaIncrement = thetaIncrement;
        rotate();
    }

    public void rotate() {
        setBounds( //
                this.circleCenterX - (int) (Math.sin(this.theta) * this.radius), //
                this.circleCenterY - (int) (Math.cos(this.theta) * this.radius), //
                (int) getPreferredSize().getWidth(), (int) getPreferredSize().getHeight());
        this.theta -= this.thetaIncrement;
    }

    public static void main(String[] args) {
        double initialTheta = Math.toRadians(0); // start north
        // double initialTheta = Math.toRadians(90); // start west
        // double initialTheta = Math.toRadians(180); // start south
        // double initialTheta = Math.toRadians(270); // start east

        final RotatingLabel label = new RotatingLabel("Foo Bar", //
                450, // circleCenterX
                325, // circleCenterY
                300, // radius
                initialTheta, //
                0.005); // thetaIncrement

        JFrame frame = new JFrame();
        frame.setSize(new Dimension(800, 700));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(label);
        frame.setVisible(true);

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        label.rotate();
                    }
                });
            }
        }, 0, 1000l / 60l); // 60 frames per second
    }
}

答案 1 :(得分:3)

同样,只需对所有象限使用一个等式。例如,此代码显示4个JLabel,每个象限中一个(每个象限从其邻居旋转Math.PI / 2),并使用相同的等式旋转 all 标签:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class RotateLabels extends JPanel{
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private static final int TIMER_DELAY = 30;
   protected static final double DELTA_THETA = Math.PI / 120;
   private JLabel[] labels = new JLabel[4];
   private double theta = -Math.PI / 4.0;

   public RotateLabels() {
      setLayout(null);
      for (int i = 0; i < labels.length; i++) {
         labels[i] = new JLabel("Label " + (i + 1));
         labels[i].setSize(labels[i].getPreferredSize());
         add(labels[i]);
      }
      setAllLabelLocations();
      new Timer(TIMER_DELAY, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            theta += DELTA_THETA;
            setAllLabelLocations();
         }
      }).start();
   }

   private void setAllLabelLocations() {
      for (int i = 0; i < labels.length; i++) {
         setLabelLocation(labels[i], i);
      }
      repaint();
   }

   private void setLabelLocation(JLabel label, int i) {
      double radius = PREF_W / 3.0;
      int w = label.getWidth();
      int h = label.getHeight();

      double angle = theta + i * Math.PI / 2.0;
      double centerX = radius * Math.cos(angle);
      double centerY = radius * Math.sin(angle);

      int x = (int)(centerX - w / 2.0) + PREF_W / 2;
      int y = (int)(centerY - h / 2.0) + PREF_H / 2;
      label.setLocation(x, y);
   }

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

   private static void createAndShowGui() {
      JFrame frame = new JFrame("Rotate");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new RotateLabels());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

请注意,此代码效率不高。如果我要创建“for real”,我可能会创建一个位置查找表,以避免在程序运行期间发生的所有浮点计算。

或更一般地说:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

@SuppressWarnings("serial")
public class RotateLabels2 extends JPanel{
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private static final int TIMER_DELAY = 10;
   protected static final double DELTA_THETA = Math.PI / 420;
   private static final int LABEL_COUNT = 12;
   private JLabel[] labels = new JLabel[LABEL_COUNT];
   private double theta = -Math.PI / 4.0;

   public RotateLabels2() {
      setLayout(null);
      for (int i = 0; i < labels.length; i++) {
         labels[i] = new JLabel("Label " + (i + 1));
         labels[i].setSize(labels[i].getPreferredSize());
         add(labels[i]);
      }
      setAllLabelLocations();
      new Timer(TIMER_DELAY, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            theta += DELTA_THETA;
            setAllLabelLocations();
         }
      }).start();
   }

   private void setAllLabelLocations() {
      for (int i = 0; i < labels.length; i++) {
         setLabelLocation(labels[i], i);
      }
      repaint();
   }

   private void setLabelLocation(JLabel label, int i) {
      double radius = PREF_W / 3.0;
      int w = label.getWidth();
      int h = label.getHeight();

      double angle = theta + i * 2 * Math.PI / labels.length;
      double centerX = radius * Math.cos(angle);
      double centerY = radius * Math.sin(angle);

      int x = (int)(centerX - w / 2.0) + PREF_W / 2;
      int y = (int)(centerY - h / 2.0) + PREF_H / 2;
      label.setLocation(x, y);
   }

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

   private static void createAndShowGui() {
      JFrame frame = new JFrame("Rotate");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new RotateLabels2());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}