2

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

这是我写的,效果很好,除了南北位置逆时针旋转。原因是,我使用了单独的三角公式来处理方向 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);
            }

有没有更有效的方法来解决这个问题?也许是一个更简单的触发方法。我能做些什么来确保所有的运动都是顺时针的?

4

2 回答 2

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
    }
}
于 2012-06-03T20:04:43.117 回答
3

同样,只需对所有象限使用一个方程。例如,这段代码显示了 4 个 JLabel,每个象限一个(每个从其邻居旋转 Math.PI/2),并且使用相同的方程来旋转所有标签:

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

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

或更一般地说:

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();
         }
      });
   }
}
于 2012-06-03T20:37:05.460 回答